mirror of https://github.com/apache/lucene.git
LUCENE-3795: Replace contrib/spatial with modules/spatial (merge from branch)
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1300409 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
commit
2ac613aef9
|
@ -14,10 +14,8 @@
|
|||
<classpathentry kind="src" path="lucene/contrib/misc/src/test"/>
|
||||
<classpathentry kind="src" path="lucene/contrib/sandbox/src/java"/>
|
||||
<classpathentry kind="src" path="lucene/contrib/sandbox/src/test"/>
|
||||
<classpathentry kind="src" path="lucene/contrib/spatial/src/java"/>
|
||||
<classpathentry kind="src" path="lucene/contrib/spatial/src/test"/>
|
||||
<classpathentry kind="src" path="lucene/test-framework/src/java"/>
|
||||
<classpathentry kind="src" output="bin.tests-framework" path="lucene/test-framework/src/resources"/>
|
||||
<classpathentry kind="src" output="bin.tests-framework" path="lucene/test-framework/src/resources"/>
|
||||
<classpathentry kind="src" path="modules/analysis/common/src/java"/>
|
||||
<classpathentry kind="src" path="modules/analysis/common/src/resources"/>
|
||||
<classpathentry kind="src" path="modules/analysis/common/src/test"/>
|
||||
|
@ -55,6 +53,10 @@
|
|||
<classpathentry kind="src" path="modules/queryparser/src/test"/>
|
||||
<classpathentry kind="src" path="modules/suggest/src/java"/>
|
||||
<classpathentry kind="src" path="modules/suggest/src/test"/>
|
||||
<classpathentry kind="src" path="modules/spatial/src/java"/>
|
||||
<classpathentry kind="src" path="modules/spatial/src/test"/>
|
||||
<classpathentry kind="src" path="modules/spatial/src/test-files"/>
|
||||
<classpathentry kind="lib" path="modules/spatial/lib/spatial4j-0.2.jar"/>
|
||||
<classpathentry kind="src" path="solr/core/src/java"/>
|
||||
<classpathentry kind="src" path="solr/core/src/test"/>
|
||||
<classpathentry kind="src" path="solr/core/src/test-files"/>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
<buildFile url="file://$PROJECT_DIR$/lucene/contrib/memory/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/contrib/misc/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/contrib/sandbox/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/contrib/spatial/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/core/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/tools/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/lucene/test-framework/build.xml" />
|
||||
|
@ -26,6 +25,7 @@
|
|||
<buildFile url="file://$PROJECT_DIR$/modules/join/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/modules/queries/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/modules/queryparser/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/modules/spatial/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/modules/suggest/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/solr/build.xml" />
|
||||
<buildFile url="file://$PROJECT_DIR$/solr/core/build.xml" />
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<module filepath="$PROJECT_DIR$/lucene/contrib/memory/memory.iml" />
|
||||
<module filepath="$PROJECT_DIR$/lucene/contrib/misc/misc.iml" />
|
||||
<module filepath="$PROJECT_DIR$/lucene/contrib/sandbox/sandbox.iml" />
|
||||
<module filepath="$PROJECT_DIR$/lucene/contrib/spatial/spatial.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/analysis/common/analysis-common.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/analysis/icu/icu.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/analysis/kuromoji/kuromoji.iml" />
|
||||
|
@ -24,6 +23,7 @@
|
|||
<module filepath="$PROJECT_DIR$/modules/join/join.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/queries/queries.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/queryparser/queryparser.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/spatial/spatial.iml" />
|
||||
<module filepath="$PROJECT_DIR$/modules/suggest/suggest.iml" />
|
||||
<module filepath="$PROJECT_DIR$/solr/solr.iml" />
|
||||
<module filepath="$PROJECT_DIR$/solr/contrib/analysis-extras/analysis-extras.iml" />
|
||||
|
|
|
@ -172,10 +172,10 @@
|
|||
<option name="VM_PARAMETERS" value="-ea -Dtests.luceneMatchVersion=4.0 -DtempDir=temp -Djetty.testMode=1 -Djetty.insecurerandom=1 -Dsolr.directoryFactory=org.apache.solr.core.MockDirectoryFactory" />
|
||||
<option name="TEST_SEARCH_SCOPE"><value defaultName="singleModule" /></option>
|
||||
</configuration>
|
||||
<configuration default="false" name="spatial contrib" type="JUnit" factoryName="JUnit">
|
||||
<configuration default="false" name="spatial module" type="JUnit" factoryName="JUnit">
|
||||
<module name="spatial" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/lucene/build/contrib/spatial" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/modules/spatial/build" />
|
||||
<option name="VM_PARAMETERS" value="-ea -DtempDir=temp" />
|
||||
<option name="TEST_SEARCH_SCOPE"><value defaultName="singleModule" /></option>
|
||||
</configuration>
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/../../build/contrib/spatial/classes/java" />
|
||||
<output-test url="file://$MODULE_DIR$/../../build/contrib/spatial/classes/test" />
|
||||
<output url="file://$MODULE_DIR$/build/classes/java" />
|
||||
<output-test url="file://$MODULE_DIR$/build/classes/test" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test-files" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library" exported="">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="file://$MODULE_DIR$/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="library" scope="TEST" name="JUnit" level="project" />
|
||||
<orderEntry type="module" module-name="misc" />
|
||||
<orderEntry type="module" module-name="lucene" />
|
||||
<orderEntry type="module" module-name="lucene" exported="" />
|
||||
<orderEntry type="module" module-name="queries" />
|
||||
<orderEntry type="module" module-name="analysis-common" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -36,7 +36,6 @@
|
|||
<module>memory</module>
|
||||
<module>misc</module>
|
||||
<module>sandbox</module>
|
||||
<module>spatial</module>
|
||||
</modules>
|
||||
<build>
|
||||
<directory>build/lucene-contrib-aggregator</directory>
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<module>join</module>
|
||||
<module>queries</module>
|
||||
<module>queryparser</module>
|
||||
<module>spatial</module>
|
||||
<module>suggest</module>
|
||||
</modules>
|
||||
<build>
|
||||
|
|
|
@ -1,83 +1,80 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-parent</artifactId>
|
||||
<version>@version@</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-spatial</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Lucene Spatial</name>
|
||||
<description>Spatial search package</description>
|
||||
<properties>
|
||||
<module-directory>lucene/contrib/spatial</module-directory>
|
||||
<build-directory>../../build/contrib/spatial</build-directory>
|
||||
</properties>
|
||||
<scm>
|
||||
<connection>
|
||||
scm:svn:http://svn.apache.org/repos/asf/lucene/dev/trunk/${module-directory}
|
||||
</connection>
|
||||
<developerConnection>
|
||||
scm:svn:https://svn.apache.org/repos/asf/lucene/dev/trunk/${module-directory}
|
||||
</developerConnection>
|
||||
<url>
|
||||
http://svn.apache.org/viewvc/lucene/dev/trunk/${module-directory}
|
||||
</url>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<!-- lucene-test-framework dependency must be declared before lucene-core -->
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-test-framework</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-queries</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<directory>${build-directory}</directory>
|
||||
<outputDirectory>${build-directory}/classes/java</outputDirectory>
|
||||
<testOutputDirectory>${build-directory}/classes/test</testOutputDirectory>
|
||||
<sourceDirectory>src/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>${project.build.testSourceDirectory}</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</testResource>
|
||||
</testResources>
|
||||
</build>
|
||||
</project>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-parent</artifactId>
|
||||
<version>@version@</version>
|
||||
<relativePath>../../../lucene/pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-spatial</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Lucene Spatial</name>
|
||||
<description>
|
||||
Spatial Strategies for Apache Lucene
|
||||
</description>
|
||||
<properties>
|
||||
<module-directory>modules/spatial</module-directory>
|
||||
<build-directory>../build</build-directory>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<!-- lucene-test-framework dependency must be declared before lucene-core -->
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-test-framework</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.spatial4j</groupId>
|
||||
<artifactId>spatial4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-queries</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>lucene-analyzers-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<directory>${build-directory}</directory>
|
||||
<outputDirectory>${build-directory}/classes/java</outputDirectory>
|
||||
<testOutputDirectory>${build-directory}/classes/test</testOutputDirectory>
|
||||
<sourceDirectory>src/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test-files</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
</build>
|
||||
</project>
|
|
@ -344,6 +344,11 @@
|
|||
<artifactId>javax.servlet</artifactId>
|
||||
<version>3.0.0.v201112011016</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.spatial4j</groupId>
|
||||
<artifactId>spatial4j</artifactId>
|
||||
<version>0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
|
|
|
@ -682,6 +682,10 @@ Optimizations
|
|||
should be rounded up for better performance. This option only applies for docvalues types bytes fixed sorted
|
||||
and bytes var sorted. (Simon Willnauer, Martijn van Groningen)
|
||||
|
||||
* LUCENE-3795: Replace contrib/spatial with modules/spatial. This includes
|
||||
a basic spatial strategy interface. (David Smiley, Chris Male, ryan)
|
||||
|
||||
|
||||
Bug fixes
|
||||
|
||||
* LUCENE-2803: The FieldCache can miss values if an entry for a reader
|
||||
|
|
|
@ -264,7 +264,6 @@
|
|||
<packageset dir="contrib/memory/src/java"/>
|
||||
<packageset dir="contrib/misc/src/java"/>
|
||||
<packageset dir="contrib/sandbox/src/java"/>
|
||||
<packageset dir="contrib/spatial/src/java"/>
|
||||
<!-- end alpha sort -->
|
||||
|
||||
<!-- If the main javadoc Group listing includes an "Other -->
|
||||
|
@ -279,7 +278,6 @@
|
|||
<group title="contrib: Memory" packages="org.apache.lucene.index.memory*"/>
|
||||
<group title="contrib: Misc " packages="org.apache.lucene.misc*"/>
|
||||
<group title="contrib: Sandbox" packages="org.apache.lucene.sandbox*"/>
|
||||
<group title="contrib: Spatial" packages="org.apache.lucene.spatial*"/>
|
||||
|
||||
</sources>
|
||||
</invoke-javadoc>
|
||||
|
|
|
@ -228,12 +228,12 @@
|
|||
<property name="sandbox.uptodate" value="true"/>
|
||||
</target>
|
||||
|
||||
<property name="spatial.jar" value="${common.dir}/build/contrib/spatial/lucene-spatial-${version}.jar"/>
|
||||
<property name="spatial.jar" value="${common.dir}/../modules/spatial/build/lucene-spatial-${version}.jar"/>
|
||||
<target name="check-spatial-uptodate" unless="spatial.uptodate">
|
||||
<contrib-uptodate name="spatial" jarfile="${spatial.jar}" property="spatial.uptodate"/>
|
||||
<module-uptodate name="spatial" jarfile="${spatial.jar}" property="spatial.uptodate"/>
|
||||
</target>
|
||||
<target name="jar-spatial" unless="spatial.uptodate" depends="check-spatial-uptodate">
|
||||
<ant dir="${common.dir}/contrib/spatial" target="jar-core" inheritAll="false">
|
||||
<ant dir="${common.dir}/../modules/spatial" target="jar-core" inheritAll="false">
|
||||
<propertyset refid="uptodate.and.compiled.properties"/>
|
||||
</ant>
|
||||
<property name="spatial.uptodate" value="true"/>
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<project name="spatial" default="default">
|
||||
|
||||
<description>
|
||||
Lucene Spatial Indexing
|
||||
</description>
|
||||
|
||||
<import file="../contrib-build.xml"/>
|
||||
|
||||
<path id="classpath">
|
||||
<pathelement path="${queries.jar}"/>
|
||||
<path refid="base.classpath"/>
|
||||
</path>
|
||||
|
||||
<target name="compile-core" depends="jar-queries, common.compile-core" />
|
||||
</project>
|
|
@ -1,465 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import org.apache.lucene.spatial.geometry.DistanceUnits;
|
||||
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
||||
import org.apache.lucene.spatial.geometry.LatLng;
|
||||
import org.apache.lucene.spatial.geometry.shape.LLRect;
|
||||
import org.apache.lucene.spatial.geometry.shape.Rectangle;
|
||||
import org.apache.lucene.spatial.tier.InvalidGeoException;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
|
||||
public class DistanceUtils {
|
||||
|
||||
public static final double DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
public static final double RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
||||
//pre-compute some angles that are commonly used
|
||||
public static final double DEG_45_AS_RADS = Math.PI / 4.0;
|
||||
public static final double SIN_45_AS_RADS = Math.sin(DEG_45_AS_RADS);
|
||||
public static final double DEG_90_AS_RADS = Math.PI / 2;
|
||||
public static final double DEG_180_AS_RADS = Math.PI;
|
||||
public static final double DEG_225_AS_RADS = 5 * DEG_45_AS_RADS;
|
||||
public static final double DEG_270_AS_RADS = 3*DEG_90_AS_RADS;
|
||||
|
||||
|
||||
public static final double KM_TO_MILES = 0.621371192;
|
||||
public static final double MILES_TO_KM = 1.609344;
|
||||
/**
|
||||
* The International Union of Geodesy and Geophysics says the Earth's mean radius in KM is:
|
||||
*
|
||||
* [1] http://en.wikipedia.org/wiki/Earth_radius
|
||||
*/
|
||||
public static final double EARTH_MEAN_RADIUS_KM = 6371.009;
|
||||
|
||||
public static final double EARTH_MEAN_RADIUS_MI = EARTH_MEAN_RADIUS_KM / MILES_TO_KM;
|
||||
|
||||
public static final double EARTH_EQUATORIAL_RADIUS_MI = 3963.205;
|
||||
public static final double EARTH_EQUATORIAL_RADIUS_KM = EARTH_EQUATORIAL_RADIUS_MI * MILES_TO_KM;
|
||||
|
||||
|
||||
public static double getDistanceMi(double x1, double y1, double x2, double y2) {
|
||||
return getLLMDistance(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x1
|
||||
* @param y1
|
||||
* @param miles
|
||||
* @return boundary rectangle where getY/getX is top left, getMinY/getMinX is bottom right
|
||||
*/
|
||||
public static Rectangle getBoundary (double x1, double y1, double miles) {
|
||||
|
||||
LLRect box = LLRect.createBox( new FloatLatLng( x1, y1 ), miles, miles );
|
||||
|
||||
//System.out.println("Box: "+maxX+" | "+ maxY+" | "+ minX + " | "+ minY);
|
||||
return box.toRectangle();
|
||||
|
||||
}
|
||||
|
||||
public static double getLLMDistance (double x1, double y1, double x2, double y2) {
|
||||
|
||||
LatLng p1 = new FloatLatLng( x1, y1 );
|
||||
LatLng p2 = new FloatLatLng( x2, y2 );
|
||||
return p1.arcDistance( p2, DistanceUnits.MILES );
|
||||
}
|
||||
|
||||
/**
|
||||
* distance/radius.
|
||||
* @param distance The distance travelled
|
||||
* @param radius The radius of the sphere
|
||||
* @return The angular distance, in radians
|
||||
*/
|
||||
public static double angularDistance(double distance, double radius){
|
||||
return distance/radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the p-norm (i.e. length) beteen two vectors
|
||||
*
|
||||
* @param vec1 The first vector
|
||||
* @param vec2 The second vector
|
||||
* @param power The power (2 for Euclidean distance, 1 for manhattan, etc.)
|
||||
* @return The length.
|
||||
* <p/>
|
||||
* See http://en.wikipedia.org/wiki/Lp_space
|
||||
* @see #vectorDistance(double[], double[], double, double)
|
||||
*/
|
||||
public static double vectorDistance(double[] vec1, double[] vec2, double power) {
|
||||
return vectorDistance(vec1, vec2, power, 1.0 / power);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the p-norm (i.e. length) between two vectors
|
||||
*
|
||||
* @param vec1 The first vector
|
||||
* @param vec2 The second vector
|
||||
* @param power The power (2 for Euclidean distance, 1 for manhattan, etc.)
|
||||
* @param oneOverPower If you've precalculated oneOverPower and cached it, use this method to save one division operation over {@link #vectorDistance(double[], double[], double)}.
|
||||
* @return The length.
|
||||
*/
|
||||
public static double vectorDistance(double[] vec1, double[] vec2, double power, double oneOverPower) {
|
||||
double result = 0;
|
||||
|
||||
if (power == 0) {
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
result += vec1[i] - vec2[i] == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
} else if (power == 1.0) {
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
result += vec1[i] - vec2[i];
|
||||
}
|
||||
} else if (power == 2.0) {
|
||||
result = Math.sqrt(squaredEuclideanDistance(vec1, vec2));
|
||||
} else if (power == Integer.MAX_VALUE || Double.isInfinite(power)) {//infinite norm?
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
result = Math.max(result, Math.max(vec1[i], vec2[i]));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
result += Math.pow(vec1[i] - vec2[i], power);
|
||||
}
|
||||
result = Math.pow(result, oneOverPower);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the coordinates of a vector that is the corner of a box (upper right or lower left), assuming a Rectangular
|
||||
* coordinate system. Note, this does not apply for points on a sphere or ellipse (although it could be used as an approximatation).
|
||||
*
|
||||
* @param center The center point
|
||||
* @param result Holds the result, potentially resizing if needed.
|
||||
* @param distance The d from the center to the corner
|
||||
* @param upperRight If true, return the coords for the upper right corner, else return the lower left.
|
||||
* @return The point, either the upperLeft or the lower right
|
||||
*/
|
||||
public static double[] vectorBoxCorner(double[] center, double[] result, double distance, boolean upperRight) {
|
||||
if (result == null || result.length != center.length) {
|
||||
result = new double[center.length];
|
||||
}
|
||||
if (upperRight == false) {
|
||||
distance = -distance;
|
||||
}
|
||||
//We don't care about the power here,
|
||||
// b/c we are always in a rectangular coordinate system, so any norm can be used by
|
||||
//using the definition of sine
|
||||
distance = SIN_45_AS_RADS * distance; // sin(Pi/4) == (2^0.5)/2 == opp/hyp == opp/distance, solve for opp, similarily for cosine
|
||||
for (int i = 0; i < center.length; i++) {
|
||||
result[i] = center[i] + distance;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param latCenter In degrees
|
||||
* @param lonCenter In degrees
|
||||
* @param distance The distance
|
||||
* @param result A preallocated array to hold the results. If null, a new one is constructed.
|
||||
* @param upperRight If true, calculate the upper right corner, else the lower left
|
||||
* @param sphereRadius The radius of the sphere to use.
|
||||
* @return The Lat/Lon in degrees
|
||||
*
|
||||
* @see #latLonCorner(double, double, double, double[], boolean, double)
|
||||
*/
|
||||
public static double[] latLonCornerDegs(double latCenter, double lonCenter,
|
||||
double distance, double [] result,
|
||||
boolean upperRight, double sphereRadius) {
|
||||
result = latLonCorner(latCenter * DEGREES_TO_RADIANS,
|
||||
lonCenter * DEGREES_TO_RADIANS, distance, result, upperRight, sphereRadius);
|
||||
result[0] = result[0] * RADIANS_TO_DEGREES;
|
||||
result[1] = result[1] * RADIANS_TO_DEGREES;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Haversine to calculate the corner of a box (upper right or lower left) that is the <i>distance</i> away, given a sphere of the specified <i>radius</i>.
|
||||
*
|
||||
* NOTE: This is not the same as calculating a box that transcribes a circle of the given distance.
|
||||
*
|
||||
* @param latCenter In radians
|
||||
* @param lonCenter In radians
|
||||
* @param distance The distance
|
||||
* @param result A preallocated array to hold the results. If null, a new one is constructed.
|
||||
* @param upperRight If true, give lat/lon for the upper right corner, else lower left
|
||||
* @param sphereRadius The radius to use for the calculation
|
||||
* @return The Lat/Lon in Radians
|
||||
|
||||
*/
|
||||
public static double[] latLonCorner(double latCenter, double lonCenter,
|
||||
double distance, double [] result, boolean upperRight, double sphereRadius) {
|
||||
// Haversine formula
|
||||
double brng = upperRight ? DEG_45_AS_RADS : DEG_225_AS_RADS;
|
||||
result = pointOnBearing(latCenter, lonCenter, distance, brng, result, sphereRadius);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a start point (startLat, startLon) and a bearing on a sphere of radius <i>sphereRadius</i>, return the destination point.
|
||||
* @param startLat The starting point latitude, in radians
|
||||
* @param startLon The starting point longitude, in radians
|
||||
* @param distance The distance to travel along the bearing. The units are assumed to be the same as the sphereRadius units, both of which is up to the caller to know
|
||||
* @param bearing The bearing, in radians. North is a 0 deg. bearing, east is 90 deg, south is 180 deg, west is 270 deg.
|
||||
* @param result A preallocated array to hold the results. If null, a new one is constructed.
|
||||
* @param sphereRadius The radius of the sphere to use for the calculation.
|
||||
* @return The destination point, in radians. First entry is latitude, second is longitude
|
||||
*/
|
||||
public static double[] pointOnBearing(double startLat, double startLon, double distance, double bearing, double[] result, double sphereRadius) {
|
||||
/*
|
||||
lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))
|
||||
lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))
|
||||
|
||||
*/
|
||||
double cosAngDist = Math.cos(distance / sphereRadius);
|
||||
double cosStartLat = Math.cos(startLat);
|
||||
double sinAngDist = Math.sin(distance / sphereRadius);
|
||||
double lat2 = Math.asin(Math.sin(startLat) * cosAngDist +
|
||||
cosStartLat * sinAngDist * Math.cos(bearing));
|
||||
|
||||
double lon2 = startLon + Math.atan2(Math.sin(bearing) * sinAngDist * cosStartLat,
|
||||
cosAngDist - Math.sin(startLat) * Math.sin(lat2));
|
||||
|
||||
/*lat2 = (lat2*180)/Math.PI;
|
||||
lon2 = (lon2*180)/Math.PI;*/
|
||||
//From Lucene. Move back to Lucene when synced
|
||||
// normalize long first
|
||||
if (result == null || result.length != 2){
|
||||
result = new double[2];
|
||||
}
|
||||
result[0] = lat2;
|
||||
result[1] = lon2;
|
||||
normLng(result);
|
||||
|
||||
// normalize lat - could flip poles
|
||||
normLat(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param latLng The lat/lon, in radians. lat in position 0, long in position 1
|
||||
*/
|
||||
public static void normLat(double[] latLng) {
|
||||
|
||||
if (latLng[0] > DEG_90_AS_RADS) {
|
||||
latLng[0] = DEG_90_AS_RADS - (latLng[0] - DEG_90_AS_RADS);
|
||||
if (latLng[1] < 0) {
|
||||
latLng[1] = latLng[1] + DEG_180_AS_RADS;
|
||||
} else {
|
||||
latLng[1] = latLng[1] - DEG_180_AS_RADS;
|
||||
}
|
||||
} else if (latLng[0] < -DEG_90_AS_RADS) {
|
||||
latLng[0] = -DEG_90_AS_RADS - (latLng[0] + DEG_90_AS_RADS);
|
||||
if (latLng[1] < 0) {
|
||||
latLng[1] = latLng[1] + DEG_180_AS_RADS;
|
||||
} else {
|
||||
latLng[1] = latLng[1] - DEG_180_AS_RADS;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized Lng rectangle shape for the bounding box
|
||||
*
|
||||
* @param latLng The lat/lon, in radians, lat in position 0, long in position 1
|
||||
*/
|
||||
public static void normLng(double[] latLng) {
|
||||
if (latLng[1] > DEG_180_AS_RADS) {
|
||||
latLng[1] = -1.0 * (DEG_180_AS_RADS - (latLng[1] - DEG_180_AS_RADS));
|
||||
} else if (latLng[1] < -DEG_180_AS_RADS) {
|
||||
latLng[1] = (latLng[1] + DEG_180_AS_RADS) + DEG_180_AS_RADS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The square of the Euclidean Distance. Not really a distance, but useful if all that matters is
|
||||
* comparing the result to another one.
|
||||
*
|
||||
* @param vec1 The first point
|
||||
* @param vec2 The second point
|
||||
* @return The squared Euclidean distance
|
||||
*/
|
||||
public static double squaredEuclideanDistance(double[] vec1, double[] vec2) {
|
||||
double result = 0;
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
double v = vec1[i] - vec2[i];
|
||||
result += v * v;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the haversine distance between two points. The arguments are in radians and provided in lat,lon order.
|
||||
* @param y1 The y coordinate of the first point, in radians
|
||||
* @param x1 The x coordinate of the first point, in radians
|
||||
* @param y2 The y coordinate of the second point, in radians
|
||||
* @param x2 The x coordinate of the second point, in radians
|
||||
* @param radius The radius of the sphere
|
||||
* @return The distance between the two points, as determined by the haversine formula.
|
||||
*/
|
||||
public static double haversine(double y1, double x1, double y2, double x2, double radius) {
|
||||
double result = 0;
|
||||
//make sure they aren't all the same, as then we can just return 0
|
||||
if ((x1 != x2) || (y1 != y2)) {
|
||||
double diffX = x1 - x2;
|
||||
double diffY = y1 - y2;
|
||||
double hsinX = Math.sin(diffX * 0.5);
|
||||
double hsinY = Math.sin(diffY * 0.5);
|
||||
double h = hsinY * hsinY +
|
||||
(Math.cos(y1) * Math.cos(y2) * hsinX * hsinX);
|
||||
result = (radius * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string containing <i>dimension</i> values encoded in it, separated by commas, return a String array of length <i>dimension</i>
|
||||
* containing the values.
|
||||
*
|
||||
* @param out A preallocated array. Must be size dimension. If it is not it will be resized.
|
||||
* @param externalVal The value to parse
|
||||
* @param dimension The expected number of values for the point
|
||||
* @return An array of the values that make up the point (aka vector)
|
||||
* @throws org.apache.lucene.spatial.tier.InvalidGeoException if the dimension specified does not match the number of values in the externalValue.
|
||||
*/
|
||||
public static String[] parsePoint(String[] out, String externalVal, int dimension) throws InvalidGeoException {
|
||||
//TODO: Should we support sparse vectors?
|
||||
if (out == null || out.length != dimension) out = new String[dimension];
|
||||
int idx = externalVal.indexOf(',');
|
||||
int end = idx;
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
if (idx == -1 && dimension == 1 && externalVal.length() > 0) {//we have a single point, dimension better be 1
|
||||
out[0] = externalVal.trim();
|
||||
i = 1;
|
||||
} else if (idx > 0) {//if it is zero, that is an error
|
||||
//Parse out a comma separated list of point values, as in: 73.5,89.2,7773.4
|
||||
for (; i < dimension; i++) {
|
||||
while (start < end && externalVal.charAt(start) == ' ') start++;
|
||||
while (end > start && externalVal.charAt(end - 1) == ' ') end--;
|
||||
if (start == end) {
|
||||
break;
|
||||
}
|
||||
out[i] = externalVal.substring(start, end);
|
||||
start = idx + 1;
|
||||
end = externalVal.indexOf(',', start);
|
||||
idx = end;
|
||||
if (end == -1) {
|
||||
end = externalVal.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i != dimension) {
|
||||
throw new InvalidGeoException("incompatible dimension (" + dimension +
|
||||
") and values (" + externalVal + "). Only " + i + " values specified");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string containing <i>dimension</i> values encoded in it, separated by commas, return a double array of length <i>dimension</i>
|
||||
* containing the values.
|
||||
*
|
||||
* @param out A preallocated array. Must be size dimension. If it is not it will be resized.
|
||||
* @param externalVal The value to parse
|
||||
* @param dimension The expected number of values for the point
|
||||
* @return An array of the values that make up the point (aka vector)
|
||||
* @throws InvalidGeoException if the dimension specified does not match the number of values in the externalValue.
|
||||
*/
|
||||
public static double[] parsePointDouble(double[] out, String externalVal, int dimension) throws InvalidGeoException{
|
||||
if (out == null || out.length != dimension) out = new double[dimension];
|
||||
int idx = externalVal.indexOf(',');
|
||||
int end = idx;
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
if (idx == -1 && dimension == 1 && externalVal.length() > 0) {//we have a single point, dimension better be 1
|
||||
out[0] = Double.parseDouble(externalVal.trim());
|
||||
i = 1;
|
||||
} else if (idx > 0) {//if it is zero, that is an error
|
||||
//Parse out a comma separated list of point values, as in: 73.5,89.2,7773.4
|
||||
for (; i < dimension; i++) {
|
||||
//TODO: abstract common code with other parsePoint
|
||||
while (start < end && externalVal.charAt(start) == ' ') start++;
|
||||
while (end > start && externalVal.charAt(end - 1) == ' ') end--;
|
||||
if (start == end) {
|
||||
break;
|
||||
}
|
||||
out[i] = Double.parseDouble(externalVal.substring(start, end));
|
||||
start = idx + 1;
|
||||
end = externalVal.indexOf(',', start);
|
||||
idx = end;
|
||||
if (end == -1) {
|
||||
end = externalVal.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i != dimension) {
|
||||
throw new InvalidGeoException("incompatible dimension (" + dimension +
|
||||
") and values (" + externalVal + "). Only " + i + " values specified");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static final double[] parseLatitudeLongitude(String latLonStr) throws InvalidGeoException {
|
||||
return parseLatitudeLongitude(null, latLonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* extract (by calling {@link #parsePoint(String[], String, int)} and validate the latitude and longitude contained
|
||||
* in the String by making sure the latitude is between 90 & -90 and longitude is between -180 and 180.
|
||||
* <p/>
|
||||
* The latitude is assumed to be the first part of the string and the longitude the second part.
|
||||
*
|
||||
* @param latLon A preallocated array to hold the result
|
||||
* @param latLonStr The string to parse. Latitude is the first value, longitude is the second.
|
||||
* @return The lat long
|
||||
* @throws InvalidGeoException if there was an error parsing
|
||||
*/
|
||||
public static final double[] parseLatitudeLongitude(double[] latLon, String latLonStr) throws InvalidGeoException {
|
||||
if (latLon == null) {
|
||||
latLon = new double[2];
|
||||
}
|
||||
double[] toks = parsePointDouble(null, latLonStr, 2);
|
||||
|
||||
if (toks[0] < -90.0 || toks[0] > 90.0) {
|
||||
throw new InvalidGeoException(
|
||||
"Invalid latitude: latitudes are range -90 to 90: provided lat: ["
|
||||
+ toks[0] + "]");
|
||||
}
|
||||
latLon[0] = toks[0];
|
||||
|
||||
|
||||
if (toks[1] < -180.0 || toks[1] > 180.0) {
|
||||
|
||||
throw new InvalidGeoException(
|
||||
"Invalid longitude: longitudes are range -180 to 180: provided lon: ["
|
||||
+ toks[1] + "]");
|
||||
}
|
||||
latLon[1] = toks[1];
|
||||
|
||||
return latLon;
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geohash;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.FieldCache;
|
||||
import org.apache.lucene.search.FieldCache.DocTerms;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.FilteredDocIdSet;
|
||||
import org.apache.lucene.spatial.DistanceUtils;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.spatial.tier.DistanceFilter;
|
||||
|
||||
|
||||
/** <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
|
||||
public class GeoHashDistanceFilter extends DistanceFilter {
|
||||
|
||||
private double lat;
|
||||
private double lng;
|
||||
private String geoHashField;
|
||||
|
||||
/**
|
||||
* Provide a distance filter based from a center point with a radius
|
||||
* in miles
|
||||
* @param startingFilter
|
||||
* @param lat
|
||||
* @param lng
|
||||
* @param miles
|
||||
*/
|
||||
public GeoHashDistanceFilter(Filter startingFilter, double lat, double lng, double miles, String geoHashField) {
|
||||
super(startingFilter, miles);
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
this.geoHashField = geoHashField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
|
||||
|
||||
final DocTerms geoHashValues = FieldCache.DEFAULT.getTerms(context.reader(), geoHashField);
|
||||
final BytesRef br = new BytesRef();
|
||||
|
||||
final int docBase = nextDocBase;
|
||||
nextDocBase += context.reader().maxDoc();
|
||||
|
||||
return new FilteredDocIdSet(startingFilter.getDocIdSet(context, acceptDocs)) {
|
||||
@Override
|
||||
public boolean match(int doc) {
|
||||
|
||||
// TODO: cutover to BytesRef so we don't have to
|
||||
// make String here
|
||||
String geoHash = geoHashValues.getTerm(doc, br).utf8ToString();
|
||||
double[] coords = GeoHashUtils.decode(geoHash);
|
||||
double x = coords[0];
|
||||
double y = coords[1];
|
||||
|
||||
// round off lat / longs if necessary
|
||||
// x = DistanceHandler.getPrecision(x, precise);
|
||||
// y = DistanceHandler.getPrecision(y, precise);
|
||||
Double cachedDistance = distanceLookupCache.get(geoHash);
|
||||
double d;
|
||||
|
||||
if (cachedDistance != null) {
|
||||
d = cachedDistance.doubleValue();
|
||||
} else {
|
||||
d = DistanceUtils.getDistanceMi(lat, lng, x, y);
|
||||
distanceLookupCache.put(geoHash, d);
|
||||
}
|
||||
|
||||
if (d < distance){
|
||||
distances.put(doc+docBase, d);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns true if <code>o</code> is equal to this. */
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof GeoHashDistanceFilter)) return false;
|
||||
GeoHashDistanceFilter other = (GeoHashDistanceFilter) o;
|
||||
|
||||
if (!this.startingFilter.equals(other.startingFilter) ||
|
||||
this.distance != other.distance ||
|
||||
this.lat != other.lat ||
|
||||
this.lng != other.lng ||
|
||||
!this.geoHashField.equals(other.geoHashField) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns a hash code value for this object.*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = Double.valueOf(distance).hashCode();
|
||||
h ^= startingFilter.hashCode();
|
||||
h ^= Double.valueOf(lat).hashCode();
|
||||
h ^= Double.valueOf(lng).hashCode();
|
||||
h ^= geoHashField.hashCode();
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geohash;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utilities for encoding and decoding geohashes. Based on
|
||||
* <a href="http://en.wikipedia.org/wiki/Geohash">http://en.wikipedia.org/wiki/Geohash</a>.
|
||||
*/
|
||||
public class GeoHashUtils {
|
||||
|
||||
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'};
|
||||
|
||||
private final static Map<Character,Integer> DECODE_MAP = new HashMap<Character,Integer>();
|
||||
|
||||
private static final int PRECISION = 12;
|
||||
private static final int[] BITS = {16, 8, 4, 2, 1};
|
||||
|
||||
static {
|
||||
for (int i = 0; i < BASE_32.length; i++) {
|
||||
DECODE_MAP.put(Character.valueOf(BASE_32[i]), Integer.valueOf(i));
|
||||
}
|
||||
}
|
||||
|
||||
private GeoHashUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given latitude and longitude into a geohash
|
||||
*
|
||||
* @param latitude Latitude to encode
|
||||
* @param longitude Longitude to encode
|
||||
* @return Geohash encoding of the longitude and latitude
|
||||
*/
|
||||
public static String encode(double latitude, double longitude) {
|
||||
double[] latInterval = {-90.0, 90.0};
|
||||
double[] lngInterval = {-180.0, 180.0};
|
||||
|
||||
final StringBuilder geohash = new StringBuilder();
|
||||
boolean isEven = true;
|
||||
|
||||
int bit = 0;
|
||||
int ch = 0;
|
||||
|
||||
while (geohash.length() < PRECISION) {
|
||||
double mid = 0.0;
|
||||
if (isEven) {
|
||||
mid = (lngInterval[0] + lngInterval[1]) / 2D;
|
||||
if (longitude > mid) {
|
||||
ch |= BITS[bit];
|
||||
lngInterval[0] = mid;
|
||||
} else {
|
||||
lngInterval[1] = mid;
|
||||
}
|
||||
} else {
|
||||
mid = (latInterval[0] + latInterval[1]) / 2D;
|
||||
if (latitude > mid) {
|
||||
ch |= BITS[bit];
|
||||
latInterval[0] = mid;
|
||||
} else {
|
||||
latInterval[1] = mid;
|
||||
}
|
||||
}
|
||||
|
||||
isEven = !isEven;
|
||||
|
||||
if (bit < 4) {
|
||||
bit++;
|
||||
} else {
|
||||
geohash.append(BASE_32[ch]);
|
||||
bit = 0;
|
||||
ch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return geohash.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the given geohash into a latitude and longitude
|
||||
*
|
||||
* @param geohash Geohash to deocde
|
||||
* @return Array with the latitude at index 0, and longitude at index 1
|
||||
*/
|
||||
public static double[] decode(String geohash) {
|
||||
final double[] latInterval = {-90.0, 90.0};
|
||||
final double[] lngInterval = {-180.0, 180.0};
|
||||
|
||||
boolean isEven = true;
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
for (int i = 0; i < geohash.length(); i++) {
|
||||
final int cd = DECODE_MAP.get(Character.valueOf(
|
||||
geohash.charAt(i))).intValue();
|
||||
|
||||
for (int mask : BITS) {
|
||||
if (isEven) {
|
||||
if ((cd & mask) != 0) {
|
||||
lngInterval[0] = (lngInterval[0] + lngInterval[1]) / 2D;
|
||||
} else {
|
||||
lngInterval[1] = (lngInterval[0] + lngInterval[1]) / 2D;
|
||||
}
|
||||
} else {
|
||||
if ((cd & mask) != 0) {
|
||||
latInterval[0] = (latInterval[0] + latInterval[1]) / 2D;
|
||||
} else {
|
||||
latInterval[1] = (latInterval[0] + latInterval[1]) / 2D;
|
||||
}
|
||||
}
|
||||
isEven = !isEven;
|
||||
}
|
||||
|
||||
}
|
||||
latitude = (latInterval[0] + latInterval[1]) / 2D;
|
||||
longitude = (lngInterval[0] + lngInterval[1]) / 2D;
|
||||
|
||||
return new double[] {latitude, longitude};
|
||||
}
|
||||
}
|
|
@ -1,22 +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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Support for <a href="http://en.wikipedia.org/wiki/Geohash">Geohash</a> encoding, decoding, and filtering.
|
||||
</body>
|
||||
</html>
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
/**
|
||||
* Represents lat/lngs as fixed point numbers translated so that all
|
||||
* world coordinates are in the first quadrant. The same fixed point
|
||||
* scale as is used for FixedLatLng is employed.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class CartesianPoint {
|
||||
private int x;
|
||||
private int y;
|
||||
|
||||
public CartesianPoint(int x, int y) {
|
||||
this.x=x;
|
||||
this.y=y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Point(" + x + "," + y + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new point translated in the x and y dimensions
|
||||
*/
|
||||
public CartesianPoint translate(int deltaX, int deltaY) {
|
||||
return new CartesianPoint(this.x+deltaX, this.y+deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + x;
|
||||
result = prime * result + y;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CartesianPoint other = (CartesianPoint) obj;
|
||||
if (x != other.x)
|
||||
return false;
|
||||
if (y != other.y)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
/**
|
||||
* Enum representing difference distance units, currently only kilometers and
|
||||
* miles
|
||||
*/
|
||||
public enum DistanceUnits {
|
||||
|
||||
MILES("miles", 3959, 24902),
|
||||
KILOMETERS("km", 6371, 40076);
|
||||
|
||||
private static final double MILES_KILOMETRES_RATIO = 1.609344;
|
||||
|
||||
private final String unit;
|
||||
|
||||
private final double earthCircumference;
|
||||
|
||||
private final double earthRadius;
|
||||
|
||||
/**
|
||||
* Creates a new DistanceUnit that represents the given unit
|
||||
*
|
||||
* @param unit Distance unit in String form
|
||||
* @param earthRadius Radius of the Earth in the specific distance unit
|
||||
* @param earthCircumfence Circumference of the Earth in the specific distance unit
|
||||
*/
|
||||
DistanceUnits(String unit, double earthRadius, double earthCircumfence) {
|
||||
this.unit = unit;
|
||||
this.earthCircumference = earthCircumfence;
|
||||
this.earthRadius = earthRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DistanceUnit which represents the given unit
|
||||
*
|
||||
* @param unit Unit whose DistanceUnit should be found
|
||||
* @return DistanceUnit representing the unit
|
||||
* @throws IllegalArgumentException if no DistanceUnit which represents the given unit is found
|
||||
*/
|
||||
public static DistanceUnits findDistanceUnit(String unit) {
|
||||
if (MILES.getUnit().equalsIgnoreCase(unit) || unit.equalsIgnoreCase("mi")) {
|
||||
return MILES;
|
||||
}
|
||||
|
||||
if (KILOMETERS.getUnit().equalsIgnoreCase(unit)) {
|
||||
return KILOMETERS;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown distance unit " + unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given distance in given DistanceUnit, to a distance in the unit represented by {@code this}
|
||||
*
|
||||
* @param distance Distance to convert
|
||||
* @param from Unit to convert the distance from
|
||||
* @return Given distance converted to the distance in the given unit
|
||||
*/
|
||||
public double convert(double distance, DistanceUnits from) {
|
||||
if (from == this) {
|
||||
return distance;
|
||||
}
|
||||
return (this == MILES) ? distance / MILES_KILOMETRES_RATIO : distance * MILES_KILOMETRES_RATIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the distance unit
|
||||
*
|
||||
* @return String representation of the distance unit
|
||||
*/
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <a href="http://en.wikipedia.org/wiki/Earth_radius">average earth radius</a>
|
||||
*
|
||||
* @return the average earth radius
|
||||
*/
|
||||
public double earthRadius() {
|
||||
return earthRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <a href="http://www.lyberty.com/encyc/articles/earth.html">circumference of the Earth</a>
|
||||
*
|
||||
* @return the circumference of the Earth
|
||||
*/
|
||||
public double earthCircumference() {
|
||||
return earthCircumference;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class FixedLatLng extends LatLng {
|
||||
public static final double SCALE_FACTOR=1000000;
|
||||
public static final int SCALE_FACTOR_INT=1000000;
|
||||
|
||||
private int lat, lng;
|
||||
private boolean normalized;
|
||||
|
||||
public FixedLatLng(int lat, int lng) {
|
||||
setLat(lat);
|
||||
setLng(lng);
|
||||
}
|
||||
|
||||
public FixedLatLng(LatLng ll) {
|
||||
this.lat=ll.getFixedLat();
|
||||
this.lng=ll.getFixedLng();
|
||||
}
|
||||
|
||||
protected void setLat(int lat) {
|
||||
if (lat>90*SCALE_FACTOR || lat<-90*SCALE_FACTOR) {
|
||||
throw new IllegalArgumentException("Illegal lattitude");
|
||||
}
|
||||
this.lat=lat;
|
||||
}
|
||||
|
||||
protected void setLng(int lng) {
|
||||
this.lng=lng;
|
||||
}
|
||||
|
||||
public static double fixedToDouble(int fixed) {
|
||||
return (fixed)/SCALE_FACTOR;
|
||||
}
|
||||
|
||||
public static int doubleToFixed(double d) {
|
||||
return (int)(d*SCALE_FACTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng copy() {
|
||||
return new FixedLatLng(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFixedLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFixedLng() {
|
||||
return lng;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLat() {
|
||||
return fixedToDouble(lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLng() {
|
||||
return fixedToDouble(lng);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFixedPoint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FixedLatLng toFixed() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatLatLng toFloat() {
|
||||
return new FloatLatLng(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNormalized() {
|
||||
return
|
||||
normalized || (
|
||||
(lng>=-180*SCALE_FACTOR_INT) &&
|
||||
(lng<=180*SCALE_FACTOR_INT)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng normalize() {
|
||||
if (isNormalized()) return this;
|
||||
|
||||
int delta=0;
|
||||
if (lng<0) delta=360*SCALE_FACTOR_INT;
|
||||
if (lng>=0) delta=-360*SCALE_FACTOR_INT;
|
||||
|
||||
int newLng=lng;
|
||||
while (newLng<=-180*SCALE_FACTOR_INT || newLng>=180*SCALE_FACTOR_INT) {
|
||||
newLng+=delta;
|
||||
}
|
||||
|
||||
FixedLatLng ret=new FixedLatLng(lat, newLng);
|
||||
ret.normalized=true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng calculateMidpoint(LatLng other) {
|
||||
return new FixedLatLng(
|
||||
(lat+other.getFixedLat())/2,
|
||||
(lng+other.getFixedLng())/2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = prime + lat;
|
||||
result = prime * result + lng;
|
||||
result = prime * result + (normalized ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
FixedLatLng other = (FixedLatLng) obj;
|
||||
if (lat != other.lat)
|
||||
return false;
|
||||
if (lng != other.lng)
|
||||
return false;
|
||||
if (normalized != other.normalized)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class FloatLatLng extends LatLng {
|
||||
private double lat;
|
||||
private double lng;
|
||||
private boolean normalized;
|
||||
|
||||
public FloatLatLng(double lat, double lng) {
|
||||
if (lat>90.0 || lat<-90.0) throw new IllegalArgumentException("Illegal latitude value " + lat);
|
||||
this.lat=lat;
|
||||
this.lng=lng;
|
||||
}
|
||||
|
||||
public FloatLatLng(LatLng ll) {
|
||||
this.lat=ll.getLat();
|
||||
this.lng=ll.getLng();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng copy() {
|
||||
return new FloatLatLng(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFixedLat() {
|
||||
return FixedLatLng.doubleToFixed(this.lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFixedLng() {
|
||||
return FixedLatLng.doubleToFixed(this.lng);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLat() {
|
||||
return this.lat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLng() {
|
||||
return this.lng;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFixedPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FixedLatLng toFixed() {
|
||||
return new FixedLatLng(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatLatLng toFloat() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNormalized() {
|
||||
return
|
||||
normalized || (
|
||||
(lng>=-180) &&
|
||||
(lng<=180)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng normalize() {
|
||||
if (isNormalized()) return this;
|
||||
|
||||
double delta=0;
|
||||
if (lng<0) delta=360;
|
||||
if (lng>=0) delta=-360;
|
||||
|
||||
double newLng=lng;
|
||||
while (newLng<=-180 || newLng>=180) {
|
||||
newLng+=delta;
|
||||
}
|
||||
|
||||
FloatLatLng ret=new FloatLatLng(lat, newLng);
|
||||
ret.normalized=true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng calculateMidpoint(LatLng other) {
|
||||
return new FloatLatLng(
|
||||
(lat+other.getLat())/2.0,
|
||||
(lng+other.getLng())/2.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(lat);
|
||||
int result = prime + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(lng);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
result = prime * result + (normalized ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
FloatLatLng other = (FloatLatLng) obj;
|
||||
if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(lng) != Double.doubleToLongBits(other.lng))
|
||||
return false;
|
||||
if (normalized != other.normalized)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base lat-lng class which can manipulate fixed point or floating
|
||||
* point based coordinates. Instances are immutable.
|
||||
*
|
||||
* @see FloatLatLng
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public abstract class LatLng {
|
||||
|
||||
public abstract boolean isNormalized();
|
||||
|
||||
public abstract boolean isFixedPoint();
|
||||
|
||||
public abstract LatLng normalize();
|
||||
|
||||
public abstract int getFixedLat();
|
||||
|
||||
public abstract int getFixedLng();
|
||||
|
||||
public abstract double getLat();
|
||||
|
||||
public abstract double getLng();
|
||||
|
||||
public abstract LatLng copy();
|
||||
|
||||
public abstract FixedLatLng toFixed();
|
||||
|
||||
public abstract FloatLatLng toFloat();
|
||||
|
||||
/**
|
||||
* Convert the lat/lng into the cartesian coordinate plane such that all
|
||||
* world coordinates are represented in the first quadrant.
|
||||
* The x dimension corresponds to latitude and y corresponds to longitude.
|
||||
* The translation starts with the normalized latlng and adds 180 to the latitude and
|
||||
* 90 to the longitude (subject to fixed point scaling).
|
||||
*/
|
||||
public CartesianPoint toCartesian() {
|
||||
LatLng ll=normalize();
|
||||
|
||||
int lat=ll.getFixedLat();
|
||||
int lng=ll.getFixedLng();
|
||||
|
||||
return new CartesianPoint(
|
||||
lng+180*FixedLatLng.SCALE_FACTOR_INT,
|
||||
lat+90*FixedLatLng.SCALE_FACTOR_INT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The inverse of toCartesian(). Always returns a FixedLatLng.
|
||||
* @param pt
|
||||
*/
|
||||
public static LatLng fromCartesian(CartesianPoint pt) {
|
||||
int lat=pt.getY() - 90 * FixedLatLng.SCALE_FACTOR_INT;
|
||||
int lng=pt.getX() - 180 * FixedLatLng.SCALE_FACTOR_INT;
|
||||
|
||||
return new FixedLatLng(lat, lng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the distance between two lat/lng's in miles.
|
||||
* Imported from mq java client.
|
||||
*
|
||||
* @param ll2
|
||||
* Second lat,lng position to calculate distance to.
|
||||
*
|
||||
* @return Returns the distance in miles.
|
||||
*/
|
||||
public double arcDistance(LatLng ll2) {
|
||||
return arcDistance(ll2, DistanceUnits.MILES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the distance between two lat/lng's in miles or meters.
|
||||
* Imported from mq java client. Variable references changed to match.
|
||||
*
|
||||
* @param ll2
|
||||
* Second lat,lng position to calculate distance to.
|
||||
* @param lUnits
|
||||
* Units to calculate distance, defaults to miles
|
||||
*
|
||||
* @return Returns the distance in meters or miles.
|
||||
*/
|
||||
public double arcDistance(LatLng ll2, DistanceUnits lUnits) {
|
||||
LatLng ll1 = normalize();
|
||||
ll2 = ll2.normalize();
|
||||
|
||||
double lat1 = ll1.getLat(), lng1 = ll1.getLng();
|
||||
double lat2 = ll2.getLat(), lng2 = ll2.getLng();
|
||||
|
||||
// Check for same position
|
||||
if (lat1 == lat2 && lng1 == lng2)
|
||||
return 0.0;
|
||||
|
||||
// Get the m_dLongitude difference. Don't need to worry about
|
||||
// crossing 180 since cos(x) = cos(-x)
|
||||
double dLon = lng2 - lng1;
|
||||
|
||||
double a = radians(90.0 - lat1);
|
||||
double c = radians(90.0 - lat2);
|
||||
double cosB = (Math.cos(a) * Math.cos(c))
|
||||
+ (Math.sin(a) * Math.sin(c) * Math.cos(radians(dLon)));
|
||||
|
||||
double radius = (lUnits == DistanceUnits.MILES) ? 3963.205/* MILERADIUSOFEARTH */
|
||||
: 6378.160187/* KMRADIUSOFEARTH */;
|
||||
|
||||
// Find angle subtended (with some bounds checking) in radians and
|
||||
// multiply by earth radius to find the arc distance
|
||||
if (cosB < -1.0)
|
||||
return 3.14159265358979323846/* PI */* radius;
|
||||
else if (cosB >= 1.0)
|
||||
return 0;
|
||||
else
|
||||
return Math.acos(cosB) * radius;
|
||||
}
|
||||
|
||||
private double radians(double a) {
|
||||
return a * 0.01745329251994;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + getLat() + "," + getLng() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the midpoint between this point an another. Respects fixed vs floating point
|
||||
* @param other
|
||||
*/
|
||||
public abstract LatLng calculateMidpoint(LatLng other);
|
||||
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object obj);
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
|
||||
/**
|
||||
* Ellipse shape. From C++ gl.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class Ellipse implements Geometry2D {
|
||||
private Point2D center;
|
||||
|
||||
/**
|
||||
* Half length of major axis
|
||||
*/
|
||||
private double a;
|
||||
|
||||
/**
|
||||
* Half length of minor axis
|
||||
*/
|
||||
private double b;
|
||||
|
||||
private double k1, k2, k3;
|
||||
|
||||
/**
|
||||
* sin of rotation angle
|
||||
*/
|
||||
private double s;
|
||||
|
||||
/**
|
||||
* cos of rotation angle
|
||||
*/
|
||||
private double c;
|
||||
|
||||
public Ellipse() {
|
||||
center = new Point2D(0, 0);
|
||||
}
|
||||
|
||||
private double SQR(double d) {
|
||||
return d * d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor given bounding rectangle and a rotation.
|
||||
*/
|
||||
public Ellipse(Point2D p1, Point2D p2, double angle) {
|
||||
center = new Point2D();
|
||||
|
||||
// Set the center
|
||||
center.x((p1.x() + p2.x()) * 0.5f);
|
||||
center.y((p1.y() + p2.y()) * 0.5f);
|
||||
|
||||
// Find sin and cos of the angle
|
||||
double angleRad = Math.toRadians(angle);
|
||||
c = Math.cos(angleRad);
|
||||
s = Math.sin(angleRad);
|
||||
|
||||
// Find the half lengths of the semi-major and semi-minor axes
|
||||
double dx = Math.abs(p2.x() - p1.x()) * 0.5;
|
||||
double dy = Math.abs(p2.y() - p1.y()) * 0.5;
|
||||
if (dx >= dy) {
|
||||
a = dx;
|
||||
b = dy;
|
||||
} else {
|
||||
a = dy;
|
||||
b = dx;
|
||||
}
|
||||
|
||||
// Find k1, k2, k3 - define when a point x,y is on the ellipse
|
||||
k1 = SQR(c / a) + SQR(s / b);
|
||||
k2 = 2 * s * c * ((1 / SQR(a)) - (1 / SQR(b)));
|
||||
k3 = SQR(s / a) + SQR(c / b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a line segment intersects the ellipse and if so finds the
|
||||
* point(s) of intersection.
|
||||
*
|
||||
* @param seg
|
||||
* Line segment to test for intersection
|
||||
* @param pt0
|
||||
* OUT - intersection point (if it exists)
|
||||
* @param pt1
|
||||
* OUT - second intersection point (if it exists)
|
||||
*
|
||||
* @return Returns the number of intersection points (0, 1, or 2).
|
||||
*/
|
||||
public int intersect(LineSegment seg, Point2D pt0, Point2D pt1) {
|
||||
if (pt0 == null)
|
||||
pt0 = new Point2D();
|
||||
if (pt1 == null)
|
||||
pt1 = new Point2D();
|
||||
|
||||
// Solution is found by parameterizing the line segment and
|
||||
// substituting those values into the ellipse equation.
|
||||
// Results in a quadratic equation.
|
||||
double x1 = center.x();
|
||||
double y1 = center.y();
|
||||
double u1 = seg.A.x();
|
||||
double v1 = seg.A.y();
|
||||
double u2 = seg.B.x();
|
||||
double v2 = seg.B.y();
|
||||
double dx = u2 - u1;
|
||||
double dy = v2 - v1;
|
||||
double q0 = k1 * SQR(u1 - x1) + k2 * (u1 - x1) * (v1 - y1) + k3
|
||||
* SQR(v1 - y1) - 1;
|
||||
double q1 = (2 * k1 * dx * (u1 - x1)) + (k2 * dx * (v1 - y1))
|
||||
+ (k2 * dy * (u1 - x1)) + (2 * k3 * dy * (v1 - y1));
|
||||
double q2 = (k1 * SQR(dx)) + (k2 * dx * dy) + (k3 * SQR(dy));
|
||||
|
||||
// Compare q1^2 to 4*q0*q2 to see how quadratic solves
|
||||
double d = SQR(q1) - (4 * q0 * q2);
|
||||
if (d < 0) {
|
||||
// Roots are complex valued. Line containing the segment does
|
||||
// not intersect the ellipse
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (d == 0) {
|
||||
// One real-valued root - line is tangent to the ellipse
|
||||
double t = -q1 / (2 * q2);
|
||||
if (0 <= t && t <= 1) {
|
||||
// Intersection occurs along line segment
|
||||
pt0.x(u1 + t * dx);
|
||||
pt0.y(v1 + t * dy);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
} else {
|
||||
// Two distinct real-valued roots. Solve for the roots and see if
|
||||
// they fall along the line segment
|
||||
int n = 0;
|
||||
double q = Math.sqrt(d);
|
||||
double t = (-q1 - q) / (2 * q2);
|
||||
if (0 <= t && t <= 1) {
|
||||
// Intersection occurs along line segment
|
||||
pt0.x(u1 + t * dx);
|
||||
pt0.y(v1 + t * dy);
|
||||
n++;
|
||||
}
|
||||
|
||||
// 2nd root
|
||||
t = (-q1 + q) / (2 * q2);
|
||||
if (0 <= t && t <= 1) {
|
||||
if (n == 0) {
|
||||
pt0.x(u1 + t * dx);
|
||||
pt0.y(v1 + t * dy);
|
||||
n++;
|
||||
} else {
|
||||
pt1.x(u1 + t * dx);
|
||||
pt1.y(v1 + t * dy);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public IntersectCase intersect(Rectangle r) {
|
||||
// Test if all 4 corners of the rectangle are inside the ellipse
|
||||
Point2D ul = new Point2D(r.MinPt().x(), r.MaxPt().y());
|
||||
Point2D ur = new Point2D(r.MaxPt().x(), r.MaxPt().y());
|
||||
Point2D ll = new Point2D(r.MinPt().x(), r.MinPt().y());
|
||||
Point2D lr = new Point2D(r.MaxPt().x(), r.MinPt().y());
|
||||
if (contains(ul) && contains(ur) && contains(ll) && contains(lr))
|
||||
return IntersectCase.CONTAINS;
|
||||
|
||||
// Test if any of the rectangle edges intersect
|
||||
Point2D pt0 = new Point2D(), pt1 = new Point2D();
|
||||
LineSegment bottom = new LineSegment(ll, lr);
|
||||
if (intersect(bottom, pt0, pt1) > 0)
|
||||
return IntersectCase.INTERSECTS;
|
||||
|
||||
LineSegment top = new LineSegment(ul, ur);
|
||||
if (intersect(top, pt0, pt1) > 0)
|
||||
return IntersectCase.INTERSECTS;
|
||||
|
||||
LineSegment left = new LineSegment(ll, ul);
|
||||
if (intersect(left, pt0, pt1) > 0)
|
||||
return IntersectCase.INTERSECTS;
|
||||
|
||||
LineSegment right = new LineSegment(lr, ur);
|
||||
if (intersect(right, pt0, pt1) > 0)
|
||||
return IntersectCase.INTERSECTS;
|
||||
|
||||
// Ellipse does not intersect any edge : since the case for the ellipse
|
||||
// containing the rectangle was considered above then if the center
|
||||
// is inside the ellipse is fully inside and if center is outside
|
||||
// the ellipse is fully outside
|
||||
return (r.contains(center)) ? IntersectCase.WITHIN
|
||||
: IntersectCase.OUTSIDE;
|
||||
}
|
||||
|
||||
public double area() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Point2D centroid() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean contains(Point2D pt) {
|
||||
// Plug in equation for ellipse, If evaluates to <= 0 then the
|
||||
// point is in or on the ellipse.
|
||||
double dx = pt.x() - center.x();
|
||||
double dy = pt.y() - center.y();
|
||||
double eq=(((k1 * SQR(dx)) + (k2 * dx * dy) + (k3 * SQR(dy)) - 1));
|
||||
|
||||
return eq<=0;
|
||||
}
|
||||
|
||||
public void translate(Vector2D v) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
||||
import org.apache.lucene.spatial.geometry.LatLng;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Lat-long rect. Instances are mutable.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class LLRect {
|
||||
private LatLng ll, ur;
|
||||
|
||||
public LLRect(LatLng ll, LatLng ur) {
|
||||
this.ll=ll;
|
||||
this.ur=ur;
|
||||
}
|
||||
|
||||
public LLRect(LLRect other) {
|
||||
this.ll=other.ll;
|
||||
this.ur=other.ur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the area in units of lat-lng squared. This is a contrived unit
|
||||
* that only has value when comparing to something else.
|
||||
*/
|
||||
public double area() {
|
||||
return Math.abs((ll.getLat()-ur.getLat()) * (ll.getLng()-ur.getLng()));
|
||||
}
|
||||
|
||||
public LatLng getLowerLeft() {
|
||||
return ll;
|
||||
}
|
||||
|
||||
public LatLng getUpperRight() {
|
||||
return ur;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + ll + ", " + ur + "}";
|
||||
}
|
||||
|
||||
public LatLng getMidpoint() {
|
||||
return ll.calculateMidpoint(ur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates a box centered at the given point with the given width and height in miles.
|
||||
* @param center
|
||||
* @param widthMi
|
||||
* @param heightMi
|
||||
*/
|
||||
public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
|
||||
double d = widthMi;
|
||||
LatLng ur = boxCorners(center, d, 45.0); // assume right angles
|
||||
LatLng ll = boxCorners(center, d, 225.0);
|
||||
|
||||
//System.err.println("boxCorners: ur " + ur.getLat() + ',' + ur.getLng());
|
||||
//System.err.println("boxCorners: cnt " + center.getLat() + ',' + center.getLng());
|
||||
//System.err.println("boxCorners: ll " + ll.getLat() + ',' + ll.getLng());
|
||||
return new LLRect(ll, ur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rectangle shape for the bounding box
|
||||
*/
|
||||
public Rectangle toRectangle() {
|
||||
return new Rectangle(ll.getLng(), ll.getLat(), ur.getLng(), ur.getLat());
|
||||
}
|
||||
|
||||
private static LatLng boxCorners(LatLng center, double d, double brngdeg) {
|
||||
double a = center.getLat();
|
||||
double b = center.getLng();
|
||||
double R = 3963.0; // radius of earth in miles
|
||||
double brng = (Math.PI*brngdeg/180);
|
||||
double lat1 = (Math.PI*a/180);
|
||||
double lon1 = (Math.PI*b/180);
|
||||
|
||||
// Haversine formula
|
||||
double lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
|
||||
Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
|
||||
double lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
|
||||
Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
|
||||
|
||||
lat2 = (lat2*180)/Math.PI;
|
||||
lon2 = (lon2*180)/Math.PI;
|
||||
|
||||
// normalize long first
|
||||
LatLng ll = normLng(lat2,lon2);
|
||||
|
||||
// normalize lat - could flip poles
|
||||
ll = normLat(ll.getLat(),ll.getLng());
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized Lat rectangle shape for the bounding box
|
||||
* If you go over the poles, you need to flip the lng value too
|
||||
*/
|
||||
private static LatLng normLat(double lat, double lng) {
|
||||
if (lat > 90.0) {
|
||||
lat = 90.0 - (lat - 90.0);
|
||||
if (lng < 0) {
|
||||
lng = lng+180;
|
||||
} else {
|
||||
lng=lng-180;
|
||||
}
|
||||
}
|
||||
else if (lat < -90.0) {
|
||||
lat = -90.0 - (lat + 90.0);
|
||||
if (lng < 0) {
|
||||
lng = lng+180;
|
||||
} else {
|
||||
lng=lng-180;
|
||||
}
|
||||
}
|
||||
LatLng ll=new FloatLatLng(lat, lng);
|
||||
return ll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized Lng rectangle shape for the bounding box
|
||||
*/
|
||||
private static LatLng normLng(double lat,double lng) {
|
||||
if (lng > 180.0) {
|
||||
lng = -1.0*(180.0 - (lng - 180.0));
|
||||
}
|
||||
else if (lng < -180.0) {
|
||||
lng = (lng + 180.0)+180.0;
|
||||
}
|
||||
LatLng ll=new FloatLatLng(lat, lng);
|
||||
return ll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((ll == null) ? 0 : ll.hashCode());
|
||||
result = prime * result + ((ur == null) ? 0 : ur.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
LLRect other = (LLRect) obj;
|
||||
if (ll == null) {
|
||||
if (other.ll != null)
|
||||
return false;
|
||||
} else if (!ll.equals(other.ll))
|
||||
return false;
|
||||
if (ur == null) {
|
||||
if (other.ur != null)
|
||||
return false;
|
||||
} else if (!ur.equals(other.ur))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
|
||||
/**
|
||||
* 2d line segment.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class LineSegment {
|
||||
public final Point2D A = new Point2D();
|
||||
public final Point2D B = new Point2D();
|
||||
|
||||
public LineSegment() {
|
||||
A.set(0, 0);
|
||||
B.set(0, 0);
|
||||
}
|
||||
|
||||
public LineSegment(Point2D p1, Point2D p2) {
|
||||
A.set(p1);
|
||||
B.set(p2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the distance of a specified point from the line segment and the
|
||||
* closest point on the segment to the specified point.
|
||||
*
|
||||
* @param P
|
||||
* Test point.
|
||||
* @param closestPt
|
||||
* (Return) Closest point on the segment to c.
|
||||
*
|
||||
* @return Returns the distance from P to the closest point on the segment.
|
||||
*/
|
||||
public double distance(Point2D P, Point2D /* out */closestPt) {
|
||||
if (closestPt == null)
|
||||
closestPt = new Point2D();
|
||||
|
||||
// Construct vector v (AB) and w (AP)
|
||||
Vector2D v = new Vector2D(A, B);
|
||||
Vector2D w = new Vector2D(A, P);
|
||||
|
||||
// Numerator of the component of w onto v. If <= 0 then A
|
||||
// is the closest point. By separating into the numerator
|
||||
// and denominator of the component we avoid a division unless
|
||||
// it is necessary.
|
||||
double n = w.dot(v);
|
||||
if (n <= 0.0f) {
|
||||
closestPt.set(A);
|
||||
return w.norm();
|
||||
}
|
||||
|
||||
// Get the denominator of the component. If the component >= 1
|
||||
// (d <= n) then point B is the closest point
|
||||
double d = v.dot(v);
|
||||
if (d <= n) {
|
||||
closestPt.set(B);
|
||||
return new Vector2D(B, P).norm();
|
||||
}
|
||||
|
||||
// Closest point is along the segment. The point is the projection of
|
||||
// w onto v.
|
||||
closestPt.set(v.mult(n / d));
|
||||
closestPt.add(A);
|
||||
return new Vector2D(closestPt, P).norm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((A == null) ? 0 : A.hashCode());
|
||||
result = prime * result + ((B == null) ? 0 : B.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
LineSegment other = (LineSegment) obj;
|
||||
if (A == null) {
|
||||
if (other.A != null)
|
||||
return false;
|
||||
} else if (!A.equals(other.A))
|
||||
return false;
|
||||
if (B == null) {
|
||||
if (other.B != null)
|
||||
return false;
|
||||
} else if (!B.equals(other.B))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
|
||||
/**
|
||||
* Point class. This type is mutable.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class Point2D {
|
||||
private double x;
|
||||
private double y;
|
||||
|
||||
public Point2D(double x, double y) {
|
||||
this.x=x;
|
||||
this.y=y;
|
||||
}
|
||||
|
||||
public Point2D() {
|
||||
this.x=0;
|
||||
this.y=0;
|
||||
}
|
||||
|
||||
public Point2D(Point2D other) {
|
||||
this.x=other.x;
|
||||
this.y=other.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + "," + y + ")";
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double x() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double y() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void x(double x) {
|
||||
this.x=x;
|
||||
}
|
||||
|
||||
public void y(double y) {
|
||||
this.y=y;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void set(double x, double y) {
|
||||
this.x=x;
|
||||
this.y=y;
|
||||
}
|
||||
|
||||
public void add(Vector2D v) {
|
||||
this.x+=v.getX();
|
||||
this.y+=v.getY();
|
||||
}
|
||||
|
||||
public void set(Point2D p1) {
|
||||
this.x=p1.getX();
|
||||
this.y=p1.getY();
|
||||
}
|
||||
|
||||
public void add(Point2D a) {
|
||||
this.x+=a.getX();
|
||||
this.y+=a.getY();
|
||||
}
|
||||
|
||||
public void set(Vector2D v) {
|
||||
this.x=v.getX();
|
||||
this.y=v.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Point2D other = (Point2D) obj;
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
|
||||
/**
|
||||
* Rectangle shape.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class Rectangle implements Geometry2D {
|
||||
private Point2D ptMin, ptMax;
|
||||
|
||||
public Rectangle() {
|
||||
ptMin=new Point2D(-1, 1);
|
||||
ptMax=new Point2D(1, 1);
|
||||
}
|
||||
|
||||
public Rectangle(Point2D ptMin, Point2D ptMax) {
|
||||
this.ptMin=new Point2D(ptMin);
|
||||
this.ptMax=new Point2D(ptMax);
|
||||
}
|
||||
|
||||
public Rectangle(double x1, double y1, double x2, double y2) {
|
||||
set(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + ptMin + "," + ptMax + "]";
|
||||
}
|
||||
|
||||
private void set(double x1, double y1, double x2, double y2) {
|
||||
this.ptMin=new Point2D(Math.min(x1, x2), Math.min(y1, y2));
|
||||
this.ptMax=new Point2D(Math.max(x1, x2), Math.max(y1, y2));
|
||||
}
|
||||
|
||||
public double area() {
|
||||
return (ptMax.getX() - ptMin.getX()) * (ptMax.getY() - ptMin.getY());
|
||||
}
|
||||
|
||||
public Point2D centroid() {
|
||||
return new Point2D( (ptMin.getX() + ptMax.getX()) / 2,
|
||||
(ptMin.getY() + ptMax.getY()) / 2);
|
||||
}
|
||||
|
||||
public boolean contains(Point2D p) {
|
||||
return p.getX() >= ptMin.getX() &&
|
||||
p.getX() <= ptMax.getX() &&
|
||||
p.getY() >= ptMin.getY() &&
|
||||
p.getY() <= ptMax.getY();
|
||||
}
|
||||
|
||||
public void translate(Vector2D v) {
|
||||
ptMin.add(v);
|
||||
ptMax.add(v);
|
||||
}
|
||||
|
||||
Point2D MinPt() {
|
||||
return ptMin;
|
||||
}
|
||||
|
||||
Point2D MaxPt() {
|
||||
return ptMax;
|
||||
}
|
||||
|
||||
public IntersectCase intersect(Rectangle r) {
|
||||
throw new UnsupportedOperationException();
|
||||
// TODO
|
||||
}
|
||||
|
||||
public Point2D getMaxPoint() {
|
||||
return ptMax;
|
||||
}
|
||||
|
||||
public Point2D getMinPoint() {
|
||||
return ptMin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((ptMax == null) ? 0 : ptMax.hashCode());
|
||||
result = prime * result + ((ptMin == null) ? 0 : ptMin.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Rectangle other = (Rectangle) obj;
|
||||
if (ptMax == null) {
|
||||
if (other.ptMax != null)
|
||||
return false;
|
||||
} else if (!ptMax.equals(other.ptMax))
|
||||
return false;
|
||||
if (ptMin == null) {
|
||||
if (other.ptMin != null)
|
||||
return false;
|
||||
} else if (!ptMin.equals(other.ptMin))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
|
||||
/**
|
||||
* 2D vector
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class Vector2D {
|
||||
private double x;
|
||||
private double y;
|
||||
|
||||
/**
|
||||
* Create a vector from the origin of the coordinate system to the given
|
||||
* point
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
public Vector2D(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vector from the origin of the coordinate system to the given
|
||||
* point
|
||||
*/
|
||||
public Vector2D(Point2D p) {
|
||||
this(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vector from one point to another
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
public Vector2D(Point2D from, Point2D to) {
|
||||
this(to.getX() - from.getX(), to.getY() - from.getY());
|
||||
}
|
||||
|
||||
public Vector2D() {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
}
|
||||
|
||||
public Vector2D(Vector2D other) {
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void set(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public boolean equals(Vector2D other) {
|
||||
return other != null && x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
public double dot(Vector2D in) {
|
||||
return ((x) * in.x) + (y * in.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector length (magnitude) squared
|
||||
*/
|
||||
public double normSqr() {
|
||||
// Cast to F to prevent overflows
|
||||
return (x * x) + (y * y);
|
||||
}
|
||||
|
||||
public double norm() {
|
||||
return Math.sqrt(normSqr());
|
||||
}
|
||||
|
||||
public Vector2D mult(double d) {
|
||||
return new Vector2D(x*d, y*d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Vector2D other = (Vector2D) obj;
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
||||
import org.apache.lucene.spatial.geometry.LatLng;
|
||||
import org.apache.lucene.spatial.geometry.shape.LLRect;
|
||||
import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
|
||||
import org.apache.lucene.spatial.tier.projections.IProjector;
|
||||
import org.apache.lucene.spatial.tier.projections.SinusoidalProjector;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class CartesianPolyFilterBuilder {
|
||||
|
||||
// Finer granularity than 1 mile isn't accurate with
|
||||
// standard java math. Also, there's already a 2nd
|
||||
// precise filter, if needed, in DistanceQueryBuilder,
|
||||
// that will make the filtering exact.
|
||||
public static final double MILES_FLOOR = 1.0;
|
||||
|
||||
private IProjector projector = new SinusoidalProjector();
|
||||
private final String tierPrefix;
|
||||
private int minTier;
|
||||
private int maxTier;
|
||||
|
||||
/**
|
||||
* @param tierPrefix The prefix for the name of the fields containing the tier info
|
||||
* @param minTierIndexed The minimum tier level indexed
|
||||
* @param maxTierIndexed The maximum tier level indexed
|
||||
*/
|
||||
public CartesianPolyFilterBuilder(String tierPrefix, int minTierIndexed, int maxTierIndexed) {
|
||||
this.tierPrefix = tierPrefix;
|
||||
this.minTier = minTierIndexed;
|
||||
this.maxTier = maxTierIndexed;
|
||||
}
|
||||
|
||||
public Shape getBoxShape(double latitude, double longitude, double miles) {
|
||||
if (miles < MILES_FLOOR) {
|
||||
miles = MILES_FLOOR;
|
||||
}
|
||||
LLRect box1 = LLRect.createBox(new FloatLatLng(latitude, longitude), miles, miles);
|
||||
LatLng ll = box1.getLowerLeft();
|
||||
LatLng ur = box1.getUpperRight();
|
||||
|
||||
double latY = ur.getLat();
|
||||
double latX = ll.getLat();
|
||||
double longY = ur.getLng();
|
||||
double longX = ll.getLng();
|
||||
double longX2 = 0.0;
|
||||
//These two if checks setup us up to deal with issues around the prime meridian and the 180th meridian
|
||||
//In these two cases, we need to get tiles (tiers) from the lower left up to the meridian and then
|
||||
//from the meridan to the upper right
|
||||
//Are we crossing the 180 deg. longitude, if so, we need to do some special things
|
||||
if (ur.getLng() < 0.0 && ll.getLng() > 0.0) {
|
||||
longX2 = ll.getLng();
|
||||
longX = -180.0;
|
||||
}
|
||||
//are we crossing the prime meridian (0 degrees)? If so, we need to account for it and boxes on both sides
|
||||
if (ur.getLng() > 0.0 && ll.getLng() < 0.0) {
|
||||
longX2 = ll.getLng();
|
||||
longX = 0.0;
|
||||
}
|
||||
|
||||
//System.err.println("getBoxShape:"+latY+"," + longY);
|
||||
//System.err.println("getBoxShape:"+latX+"," + longX);
|
||||
CartesianTierPlotter ctp = new CartesianTierPlotter(2, projector, tierPrefix);
|
||||
int bestFit = ctp.bestFit(miles);
|
||||
if (bestFit < minTier) {
|
||||
bestFit = minTier;
|
||||
} else if (bestFit > maxTier) {
|
||||
bestFit = maxTier;
|
||||
}
|
||||
|
||||
ctp = new CartesianTierPlotter(bestFit, projector, tierPrefix);
|
||||
Shape shape = new Shape(ctp.getTierFieldName());
|
||||
|
||||
// generate shape
|
||||
// iterate from startX->endX
|
||||
// iterate from startY -> endY
|
||||
// shape.add(currentLat.currentLong);
|
||||
//for the edge cases (prime meridian and the 180th meridian), this call handles all tiles East of the meridian
|
||||
//for all other cases, it handles the whole set of tiles
|
||||
shape = getShapeLoop(shape, ctp, latX, longX, latY, longY);
|
||||
if (longX2 != 0.0) {
|
||||
if (longX == 0.0) {
|
||||
longX = longX2;
|
||||
longY = 0.0;
|
||||
//handles the lower left longitude to the prime meridian
|
||||
//shape = getShapeLoop(shape, ctp, latX, longX, latY, longY);
|
||||
} else {
|
||||
//this clause handles the lower left longitude up to the 180 meridian
|
||||
longX = longX2;
|
||||
longY = 180.0;
|
||||
}
|
||||
shape = getShapeLoop(shape, ctp, latX, longX, latY, longY);
|
||||
|
||||
//System.err.println("getBoxShape2:"+latY+"," + longY);
|
||||
//System.err.println("getBoxShape2:"+latX+"," + longX);
|
||||
}
|
||||
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
public Shape getShapeLoop(Shape shape, CartesianTierPlotter ctp, double latX, double longX, double latY, double longY) {
|
||||
|
||||
//System.err.println("getShapeLoop:"+latY+"," + longY);
|
||||
//System.err.println("getShapeLoop:"+latX+"," + longX);
|
||||
double beginAt = ctp.getTierBoxId(latX, longX);
|
||||
double endAt = ctp.getTierBoxId(latY, longY);
|
||||
if (beginAt > endAt) {
|
||||
double tmp = beginAt;
|
||||
beginAt = endAt;
|
||||
endAt = tmp;
|
||||
}
|
||||
double tierVert = ctp.getTierVerticalPosDivider();
|
||||
//System.err.println(" | "+ beginAt+" | "+ endAt);
|
||||
|
||||
double startX = beginAt - (beginAt % 1);
|
||||
double startY = beginAt - startX; //should give a whole number
|
||||
|
||||
double endX = endAt - (endAt % 1);
|
||||
double endY = endAt - endX; //should give a whole number
|
||||
|
||||
int scale = (int) Math.log10(tierVert);
|
||||
endY = new BigDecimal(endY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
||||
startY = new BigDecimal(startY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
||||
double xInc = 1.0d / tierVert;
|
||||
xInc = new BigDecimal(xInc).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
||||
|
||||
//System.err.println("go from startX:"+startX+" to:" + endX);
|
||||
for (; startX <= endX; startX++) {
|
||||
|
||||
double itY = startY;
|
||||
//System.err.println("go from startY:"+startY+" to:" + endY);
|
||||
while (itY <= endY) {
|
||||
//create a boxId
|
||||
// startX.startY
|
||||
double boxId = startX + itY;
|
||||
shape.addBox(boxId);
|
||||
//System.err.println("----"+startX+" and "+itY);
|
||||
//System.err.println("----"+boxId);
|
||||
itY += xInc;
|
||||
|
||||
// java keeps 0.0001 as 1.0E-1
|
||||
// which ends up as 0.00011111
|
||||
itY = new BigDecimal(itY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
||||
}
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
public Filter getBoundingArea(double latitude, double longitude, double miles) {
|
||||
Shape shape = getBoxShape(latitude, longitude, miles);
|
||||
return new CartesianShapeFilter(shape, shape.getTierId());
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.index.DocsEnum;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.FixedBitSet;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class CartesianShapeFilter extends Filter {
|
||||
|
||||
private final Shape shape;
|
||||
private final String fieldName;
|
||||
|
||||
CartesianShapeFilter(final Shape shape, final String fieldName){
|
||||
this.shape = shape;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocIdSet getDocIdSet(final AtomicReaderContext context, final Bits acceptDocs) throws IOException {
|
||||
final List<Double> area = shape.getArea();
|
||||
final int sz = area.size();
|
||||
|
||||
// iterate through each boxid
|
||||
final BytesRef bytesRef = new BytesRef(NumericUtils.BUF_SIZE_LONG);
|
||||
if (sz == 1) {
|
||||
double boxId = area.get(0).doubleValue();
|
||||
NumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(boxId), 0, bytesRef);
|
||||
return new DocIdSet() {
|
||||
@Override
|
||||
public DocIdSetIterator iterator() throws IOException {
|
||||
return context.reader().termDocsEnum(acceptDocs, fieldName, bytesRef, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
final FixedBitSet bits = new FixedBitSet(context.reader().maxDoc());
|
||||
for (int i =0; i< sz; i++) {
|
||||
double boxId = area.get(i).doubleValue();
|
||||
NumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(boxId), 0, bytesRef);
|
||||
final DocsEnum docsEnum = context.reader().termDocsEnum(acceptDocs, fieldName, bytesRef, false);
|
||||
if (docsEnum == null) continue;
|
||||
// iterate through all documents
|
||||
// which have this boxId
|
||||
int doc;
|
||||
while ((doc = docsEnum.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
|
||||
bits.set(doc);
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.FieldComparatorSource;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class DistanceFieldComparatorSource extends FieldComparatorSource {
|
||||
|
||||
private DistanceFilter distanceFilter;
|
||||
private DistanceScoreDocLookupComparator dsdlc;
|
||||
|
||||
public DistanceFieldComparatorSource(Filter distanceFilter) {
|
||||
this.distanceFilter = (DistanceFilter) distanceFilter;
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
distanceFilter = null;
|
||||
|
||||
if (dsdlc != null) {
|
||||
dsdlc.cleanUp();
|
||||
}
|
||||
|
||||
dsdlc = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldComparator newComparator(String fieldname, int numHits,
|
||||
int sortPos, boolean reversed) throws IOException {
|
||||
dsdlc = new DistanceScoreDocLookupComparator(numHits);
|
||||
return dsdlc;
|
||||
}
|
||||
|
||||
private class DistanceScoreDocLookupComparator extends FieldComparator<Double> {
|
||||
|
||||
private double[] values;
|
||||
private double bottom;
|
||||
private int offset =0;
|
||||
|
||||
public DistanceScoreDocLookupComparator(int numHits) {
|
||||
values = new double[numHits];
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(int slot1, int slot2) {
|
||||
double a = values[slot1];
|
||||
double b = values[slot2];
|
||||
if (a > b)
|
||||
return 1;
|
||||
if (a < b)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
distanceFilter = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareBottom(int doc) {
|
||||
double v2 = distanceFilter.getDistance(doc+ offset);
|
||||
|
||||
if (bottom > v2) {
|
||||
return 1;
|
||||
} else if (bottom < v2) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(int slot, int doc) {
|
||||
values[slot] = distanceFilter.getDistance(doc + offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottom(int slot) {
|
||||
this.bottom = values[slot];
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldComparator setNextReader(AtomicReaderContext context)
|
||||
throws IOException {
|
||||
// each reader in a segmented base
|
||||
// has an offset based on the maxDocs of previous readers
|
||||
offset = context.docBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double value(int slot) {
|
||||
return values[slot];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/** Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.spatial.tier.DistanceHandler.Precision;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public abstract class DistanceFilter extends Filter {
|
||||
|
||||
final protected Filter startingFilter;
|
||||
protected Precision precise;
|
||||
protected Map<Integer,Double> distances;
|
||||
protected double distance;
|
||||
|
||||
protected int nextDocBase;
|
||||
protected transient WeakHashMap<String,Double> distanceLookupCache;
|
||||
|
||||
/** Filters the startingFilter by precise distance
|
||||
* checking filter */
|
||||
public DistanceFilter(Filter startingFilter, double distance) {
|
||||
if (startingFilter == null) {
|
||||
throw new IllegalArgumentException("please provide a non-null startingFilter; you can use QueryWrapperFilter(MatchAllDocsQuery) as a no-op filter");
|
||||
}
|
||||
this.startingFilter = startingFilter;
|
||||
this.distance = distance;
|
||||
|
||||
// NOTE: neither of the distance filters use precision
|
||||
// now - if we turn that on, we'll need to pass top
|
||||
// reader into here
|
||||
// setPrecision(reader.maxDoc());
|
||||
|
||||
/* store calculated distances for reuse by other components */
|
||||
distances = new HashMap<Integer,Double>();
|
||||
|
||||
// create an intermediate cache to avoid recomputing
|
||||
// distances for the same point
|
||||
// TODO: Why is this a WeakHashMap?
|
||||
distanceLookupCache = new WeakHashMap<String,Double>();
|
||||
}
|
||||
|
||||
public Map<Integer,Double> getDistances(){
|
||||
return distances;
|
||||
}
|
||||
|
||||
public Double getDistance(int docid){
|
||||
return distances.get(docid);
|
||||
}
|
||||
|
||||
public void setDistances(Map<Integer, Double> distances) {
|
||||
this.distances = distances;
|
||||
}
|
||||
|
||||
/** You must call this before re-using this DistanceFilter
|
||||
* across searches */
|
||||
public void reset() {
|
||||
nextDocBase = 0;
|
||||
}
|
||||
|
||||
/** Returns true if <code>o</code> is equal to this. */
|
||||
@Override
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
/** Returns a hash code value for this object.*/
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
/*
|
||||
private void setPrecision(int maxDocs) {
|
||||
precise = Precision.EXACT;
|
||||
|
||||
if (maxDocs > 1000 && distance > 10) {
|
||||
precise = Precision.TWENTYFEET;
|
||||
}
|
||||
|
||||
if (maxDocs > 10000 && distance > 10){
|
||||
precise = Precision.TWOHUNDREDFEET;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import org.apache.lucene.spatial.DistanceUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provide a high level access point to distances
|
||||
* Used by DistanceSortSource and DistanceQuery
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*
|
||||
*/
|
||||
public class DistanceHandler {
|
||||
|
||||
public enum Precision {EXACT, TWOFEET, TWENTYFEET, TWOHUNDREDFEET}
|
||||
|
||||
private Map<Integer,Double> distances;
|
||||
private Map<String, Double> distanceLookupCache;
|
||||
private Precision precise;
|
||||
|
||||
public DistanceHandler (Map<Integer,Double> distances, Map<String, Double> distanceLookupCache, Precision precise){
|
||||
this.distances = distances;
|
||||
this.distanceLookupCache = distanceLookupCache;
|
||||
this.precise = precise;
|
||||
}
|
||||
|
||||
|
||||
public static double getPrecision(double x, Precision thisPrecise){
|
||||
|
||||
if(thisPrecise != null){
|
||||
double dif = 0;
|
||||
switch(thisPrecise) {
|
||||
case EXACT: return x;
|
||||
case TWOFEET: dif = x % 0.0001; break;
|
||||
case TWENTYFEET: dif = x % 0.001; break;
|
||||
case TWOHUNDREDFEET: dif = x % 0.01; break;
|
||||
}
|
||||
return x - dif;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public Precision getPrecision() {
|
||||
return precise;
|
||||
}
|
||||
|
||||
public double getDistance(int docid, double centerLat, double centerLng, double lat, double lng){
|
||||
|
||||
// check to see if we have distances
|
||||
// if not calculate the distance
|
||||
if(distances == null){
|
||||
return DistanceUtils.getDistanceMi(centerLat, centerLng, lat, lng);
|
||||
}
|
||||
|
||||
// check to see if the doc id has a cached distance
|
||||
Double docd = distances.get( docid );
|
||||
if (docd != null){
|
||||
return docd.doubleValue();
|
||||
}
|
||||
|
||||
//check to see if we have a precision code
|
||||
// and if another lat/long has been calculated at
|
||||
// that rounded location
|
||||
if (precise != null) {
|
||||
double xLat = getPrecision(lat, precise);
|
||||
double xLng = getPrecision(lng, precise);
|
||||
|
||||
String k = Double.valueOf(xLat).toString() +","+ Double.valueOf(xLng).toString();
|
||||
|
||||
Double d = (distanceLookupCache.get(k));
|
||||
if (d != null){
|
||||
return d.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
//all else fails calculate the distances
|
||||
return DistanceUtils.getDistanceMi(centerLat, centerLng, lat, lng);
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.queries.ChainedFilter;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.QueryWrapperFilter;
|
||||
import org.apache.lucene.spatial.geohash.GeoHashDistanceFilter;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class DistanceQueryBuilder {
|
||||
|
||||
private final double lat;
|
||||
private final double lng;
|
||||
private final double miles;
|
||||
private final Filter filter;
|
||||
final DistanceFilter distanceFilter;
|
||||
|
||||
/**
|
||||
* Create a distance query using
|
||||
* a boundary box wrapper around a more precise
|
||||
* DistanceFilter.
|
||||
*
|
||||
* @param lat The latitude to search around
|
||||
* @param lng the Longitude to search around
|
||||
* @param miles The radius to search within
|
||||
* @param latField The name of the field containing the latitude
|
||||
* @param lngField The name of the field containing the longitude
|
||||
* @param tierFieldPrefix The prefix of the tier
|
||||
* @param needPrecise if true, then distance is calculated in addition to tier info
|
||||
* @param minTierIndexed The minimum tier level indexed
|
||||
* @param maxTierIndexed The maximum tier level indexed
|
||||
*/
|
||||
public DistanceQueryBuilder (double lat, double lng, double miles,
|
||||
String latField, String lngField, String tierFieldPrefix, boolean needPrecise, int minTierIndexed, int maxTierIndexed) {
|
||||
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
this.miles = miles;
|
||||
|
||||
CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix, minTierIndexed, maxTierIndexed);
|
||||
Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
|
||||
|
||||
/* create precise distance filter */
|
||||
if (needPrecise) {
|
||||
filter = distanceFilter = new LatLongDistanceFilter(cartesianFilter, lat, lng, miles, latField, lngField);
|
||||
} else {
|
||||
filter = cartesianFilter;
|
||||
distanceFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a distance query using
|
||||
* a boundary box wrapper around a more precise
|
||||
* DistanceFilter.
|
||||
*
|
||||
* @param lat
|
||||
* @param lng
|
||||
* @param miles
|
||||
*/
|
||||
public DistanceQueryBuilder (double lat, double lng, double miles,
|
||||
String geoHashFieldPrefix, String tierFieldPrefix, boolean needPrecise, int minTierIndexed, int maxTierIndexed){
|
||||
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
this.miles = miles;
|
||||
|
||||
CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix, minTierIndexed, maxTierIndexed);
|
||||
Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
|
||||
|
||||
/* create precise distance filter */
|
||||
if (needPrecise) {
|
||||
filter = distanceFilter = new GeoHashDistanceFilter(cartesianFilter, lat, lng, miles, geoHashFieldPrefix);
|
||||
} else {
|
||||
filter = cartesianFilter;
|
||||
distanceFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a distance query using
|
||||
* a boundary box wrapper around a more precise
|
||||
* DistanceFilter.
|
||||
*/
|
||||
public Filter getFilter() {
|
||||
if (distanceFilter != null) {
|
||||
distanceFilter.reset();
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public Filter getFilter(Query query) {
|
||||
// Chain the Query (as filter) with our distance filter
|
||||
if (distanceFilter != null) {
|
||||
distanceFilter.reset();
|
||||
}
|
||||
QueryWrapperFilter qf = new QueryWrapperFilter(query);
|
||||
return new ChainedFilter(new Filter[] {qf, filter},
|
||||
ChainedFilter.AND);
|
||||
}
|
||||
|
||||
public DistanceFilter getDistanceFilter() {
|
||||
return distanceFilter;
|
||||
}
|
||||
|
||||
public Query getQuery(Query query){
|
||||
return new ConstantScoreQuery(getFilter(query));
|
||||
}
|
||||
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public double getLng() {
|
||||
return lng;
|
||||
}
|
||||
|
||||
public double getMiles() {
|
||||
return miles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistanceQuery lat: " + lat + " lng: " + lng + " miles: "+ miles;
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.FilteredDocIdSet;
|
||||
import org.apache.lucene.search.FieldCache;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.spatial.DistanceUtils;
|
||||
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class LatLongDistanceFilter extends DistanceFilter {
|
||||
|
||||
double lat;
|
||||
double lng;
|
||||
String latField;
|
||||
String lngField;
|
||||
|
||||
int nextOffset = 0;
|
||||
|
||||
/**
|
||||
* Provide a distance filter based from a center point with a radius
|
||||
* in miles.
|
||||
* @param startingFilter Filter to start from
|
||||
* @param lat
|
||||
* @param lng
|
||||
* @param miles
|
||||
* @param latField
|
||||
* @param lngField
|
||||
*/
|
||||
public LatLongDistanceFilter(Filter startingFilter, double lat, double lng, double miles, String latField, String lngField) {
|
||||
super(startingFilter, miles);
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
this.latField = latField;
|
||||
this.lngField = lngField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
|
||||
|
||||
final double[] latIndex = FieldCache.DEFAULT.getDoubles(context.reader(), latField, false);
|
||||
final double[] lngIndex = FieldCache.DEFAULT.getDoubles(context.reader(), lngField, false);
|
||||
|
||||
final int docBase = nextDocBase;
|
||||
nextDocBase += context.reader().maxDoc();
|
||||
|
||||
return new FilteredDocIdSet(startingFilter.getDocIdSet(context, acceptDocs)) {
|
||||
@Override
|
||||
protected boolean match(int doc) {
|
||||
double x = latIndex[doc];
|
||||
double y = lngIndex[doc];
|
||||
|
||||
// round off lat / longs if necessary
|
||||
// x = DistanceHandler.getPrecision(x, precise);
|
||||
// y = DistanceHandler.getPrecision(y, precise);
|
||||
|
||||
String ck = Double.toString(x)+","+Double.toString(y);
|
||||
Double cachedDistance = distanceLookupCache.get(ck);
|
||||
|
||||
double d;
|
||||
if (cachedDistance != null){
|
||||
d = cachedDistance.doubleValue();
|
||||
} else {
|
||||
d = DistanceUtils.getDistanceMi(lat, lng, x, y);
|
||||
distanceLookupCache.put(ck, d);
|
||||
}
|
||||
|
||||
if (d < distance) {
|
||||
// Save distances, so they can be pulled for
|
||||
// sorting after filtering is done:
|
||||
distances.put(doc+docBase, d);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns true if <code>o</code> is equal to this. */
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof LatLongDistanceFilter)) return false;
|
||||
LatLongDistanceFilter other = (LatLongDistanceFilter) o;
|
||||
|
||||
if (!this.startingFilter.equals(other.startingFilter) ||
|
||||
this.distance != other.distance ||
|
||||
this.lat != other.lat ||
|
||||
this.lng != other.lng ||
|
||||
!this.latField.equals(other.latField) ||
|
||||
!this.lngField.equals(other.lngField)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns a hash code value for this object.*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = Double.valueOf(distance).hashCode();
|
||||
h ^= startingFilter.hashCode();
|
||||
h ^= Double.valueOf(lat).hashCode();
|
||||
h ^= Double.valueOf(lng).hashCode();
|
||||
h ^= latField.hashCode();
|
||||
h ^= lngField.hashCode();
|
||||
return h;
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier.projections;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class CartesianTierPlotter {
|
||||
public static final String DEFALT_FIELD_PREFIX = "_tier_";
|
||||
|
||||
|
||||
private static final double LOG_2 = Math.log(2);
|
||||
|
||||
final int tierLevel;
|
||||
int tierLength;
|
||||
int tierBoxes;
|
||||
int tierVerticalPosDivider;
|
||||
final IProjector projector;
|
||||
final String fieldPrefix;
|
||||
Double idd = Double.valueOf(180);
|
||||
|
||||
public CartesianTierPlotter (int tierLevel, IProjector projector, String fieldPrefix) {
|
||||
|
||||
this.tierLevel = tierLevel;
|
||||
this.projector = projector;
|
||||
this.fieldPrefix = fieldPrefix;
|
||||
|
||||
setTierLength();
|
||||
setTierBoxes();
|
||||
setTierVerticalPosDivider();
|
||||
}
|
||||
|
||||
private void setTierLength (){
|
||||
this.tierLength = (int) Math.pow(2 , this.tierLevel);
|
||||
}
|
||||
|
||||
private void setTierBoxes () {
|
||||
this.tierBoxes = (int)Math.pow(this.tierLength, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nearest max power of 10 greater than
|
||||
* the tierlen
|
||||
* e.g
|
||||
* tierId of 13 has tierLen 8192
|
||||
* nearest max power of 10 greater than tierLen
|
||||
* would be 10,000
|
||||
*/
|
||||
|
||||
private void setTierVerticalPosDivider() {
|
||||
|
||||
// ceiling of log base 10 of tierLen
|
||||
|
||||
tierVerticalPosDivider = Double.valueOf(Math.ceil(
|
||||
Math.log10(Integer.valueOf(this.tierLength).doubleValue()))).intValue();
|
||||
|
||||
//
|
||||
tierVerticalPosDivider = (int)Math.pow(10, tierVerticalPosDivider );
|
||||
|
||||
}
|
||||
|
||||
public double getTierVerticalPosDivider(){
|
||||
return tierVerticalPosDivider;
|
||||
}
|
||||
|
||||
/**
|
||||
* TierBoxId is latitude box id + longitude box id
|
||||
* where latitude box id, and longitude box id are transposed in to position
|
||||
* coordinates.
|
||||
*
|
||||
* @param latitude
|
||||
* @param longitude
|
||||
*/
|
||||
public double getTierBoxId (double latitude, double longitude) {
|
||||
|
||||
double[] coords = projector.coords(latitude, longitude);
|
||||
|
||||
double id = getBoxId(coords[0]) + (getBoxId(coords[1]) / tierVerticalPosDivider);
|
||||
return id ;
|
||||
}
|
||||
|
||||
|
||||
private double getBoxId (double coord){
|
||||
|
||||
|
||||
return Math.floor(coord / (idd / this.tierLength));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private double getBoxId (double coord, int tierLen){
|
||||
return Math.floor(coord / (idd / tierLen) );
|
||||
}
|
||||
/**
|
||||
* get the string name representing current tier
|
||||
* _localTier<tiedId>
|
||||
*/
|
||||
public String getTierFieldName (){
|
||||
|
||||
return fieldPrefix + this.tierLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the string name representing tierId
|
||||
* _localTier<tierId>
|
||||
* @param tierId
|
||||
*/
|
||||
public String getTierFieldName (int tierId){
|
||||
|
||||
return fieldPrefix + tierId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the tier with the best fit for a bounding box
|
||||
* Best fit is defined as the ceiling of
|
||||
* log2 (circumference of earth / distance)
|
||||
* distance is defined as the smallest box fitting
|
||||
* the corner between a radius and a bounding box.
|
||||
*
|
||||
* Distances less than a mile return 15, finer granularity is
|
||||
* in accurate
|
||||
*/
|
||||
public int bestFit(double miles){
|
||||
|
||||
//28,892 a rough circumference of the earth
|
||||
int circ = 28892;
|
||||
|
||||
double r = miles / 2.0;
|
||||
|
||||
double corner = r - Math.sqrt(Math.pow(r, 2) / 2.0d);
|
||||
double times = circ / corner;
|
||||
int bestFit = (int)Math.ceil(log2(times)) + 1;
|
||||
|
||||
if (bestFit > 15) {
|
||||
// 15 is the granularity of about 1 mile
|
||||
// finer granularity isn't accurate with standard java math
|
||||
return 15;
|
||||
}
|
||||
return bestFit;
|
||||
}
|
||||
|
||||
/**
|
||||
* a log to the base 2 formula
|
||||
* <code>Math.log(value) / Math.log(2)</code>
|
||||
* @param value
|
||||
*/
|
||||
public static double log2(double value) {
|
||||
return Math.log(value) / LOG_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the tier level plotting is occuring at
|
||||
*
|
||||
* @return ID of the tier level plotting is occuring at
|
||||
*/
|
||||
public int getTierLevelId() {
|
||||
return this.tierLevel;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier.projections;
|
||||
|
||||
/**
|
||||
* Based on Sinusoidal Projections
|
||||
* Project a latitude / longitude on a 2D cartesian map
|
||||
* <p/>
|
||||
* THIS PROJECTION IS WRONG, but it's not going to be fixed b/c it will break a lot of existing tests, plus we are deprecating
|
||||
* most of the existing spatial and replacing with a more reliable approach.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*
|
||||
* @deprecated (3.1) Until we can put in place proper tests and a proper fix.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SinusoidalProjector implements IProjector {
|
||||
|
||||
|
||||
public String coordsAsString(double latitude, double longitude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public double[] coords(double latitude, double longitude) {
|
||||
double rlat = Math.toRadians(latitude);
|
||||
double rlong = Math.toRadians(longitude);
|
||||
double nlat = rlong * Math.cos(rlat);
|
||||
double r[] = {nlat, rlong};
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
This whole file should really be:*/
|
||||
|
||||
/**
|
||||
* Based on Sinusoidal Projections
|
||||
* Project a latitude / longitude on a 2D cartesian map using the Prime Meridian as the "central meridian"
|
||||
*
|
||||
* See http://en.wikipedia.org/wiki/Sinusoidal_projection
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
/*
|
||||
public class SinusoidalProjector implements IProjector {
|
||||
|
||||
|
||||
public String coordsAsString(double latitude, double longitude) {
|
||||
double [] coords = coords(latitude, longitude);
|
||||
return coords[0] + "," + coords[1];
|
||||
}
|
||||
|
||||
public double[] coords(double latitude, double longitude) {
|
||||
double rlat = latitude * DistanceUtils.DEGREES_TO_RADIANS;
|
||||
double rlong = longitude * DistanceUtils.DEGREES_TO_RADIANS;
|
||||
double x = rlong * Math.cos(rlat);
|
||||
return new double[]{x, rlat};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Geographical filtering & sorting with Lucene
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>This package makes it possible to filter and sort according to
|
||||
geographical constraints. For example, filter to include only
|
||||
restaurants within 2 miles of a specified latitude/longitude, sorting
|
||||
by distance ascending.</p>
|
||||
|
||||
<p>See <a href="http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html">here</a>
|
||||
for details on the technical approach.</p>
|
||||
|
||||
<p>Unfortunately, this package is still very new, and has little to no
|
||||
documentation. It's best to ask for pointers on
|
||||
java-user@lucene.apache.org, and look at the unit tests included in
|
||||
the source distribution.</p>
|
||||
|
||||
<p>There are also known issues, eg at
|
||||
least <a href="https://issues.apache.org/jira/browse/LUCENE-1815">LUCENE-1781</a>
|
||||
and <a href="https://issues.apache.org/jira/browse/LUCENE-1815">LUCENE-1815</a>.</p>
|
||||
|
||||
<p><font color="red"><b>NOTE:</b> This package is still in flux and
|
||||
might change in incompatible ways in the next release.</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
package org.apache.lucene.spatial;
|
||||
|
||||
import org.apache.lucene.spatial.tier.InvalidGeoException;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
public class DistanceUtilsTest extends LuceneTestCase {
|
||||
|
||||
public void testBoxCorner() throws Exception {
|
||||
double[] zero = new double[]{0, 0};
|
||||
double[] zeroOne = new double[]{0, 1};
|
||||
double[] oneOne = new double[]{1, 1};
|
||||
double[] pt1 = new double[]{1.5, 110.3};
|
||||
double[] result = DistanceUtils.vectorBoxCorner(zero, null, Math.sqrt(2), true);
|
||||
assertEquals(1.0, result[0], 0);
|
||||
assertEquals(1.0, result[1], 0);
|
||||
|
||||
result = DistanceUtils.vectorBoxCorner(zero, null, Math.sqrt(2), false);
|
||||
assertEquals(-1.0, result[0], 0);
|
||||
assertEquals(-1.0, result[1], 0);
|
||||
|
||||
result = DistanceUtils.vectorBoxCorner(oneOne, null, Math.sqrt(2), true);
|
||||
assertEquals(2.0, result[0], 0);
|
||||
assertEquals(2.0, result[1], 0);
|
||||
|
||||
result = DistanceUtils.vectorBoxCorner(zeroOne, null, Math.sqrt(2), true);
|
||||
assertEquals(1.0, result[0], 0);
|
||||
assertEquals(2.0, result[1], 0);
|
||||
|
||||
result = DistanceUtils.vectorBoxCorner(pt1, null, Math.sqrt(2), true);
|
||||
assertEquals(2.5, result[0], 0.1);
|
||||
assertEquals(111.3, result[1], 0.1);
|
||||
|
||||
result = DistanceUtils.vectorBoxCorner(pt1, null, Math.sqrt(2), false);
|
||||
assertEquals(0.5, result[0], 0.1);
|
||||
assertEquals(109.3, result[1], 0.1);
|
||||
|
||||
}
|
||||
|
||||
public void testNormLatLon() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
public void testLatLonCorner() throws Exception {
|
||||
double[] zero = new double[]{0, 0};
|
||||
double[] zero45 = new double[]{0, DistanceUtils.DEG_45_AS_RADS};
|
||||
double[] result;
|
||||
// 00°38′09″N, 000°38′09″E
|
||||
//Verify at http://www.movable-type.co.uk/scripts/latlong.html
|
||||
result = DistanceUtils.latLonCorner(zero[0], zero[1], 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.latLonCornerDegs(zero[0], zero[1], 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
// 00°38′09″N, 000°38′09″E
|
||||
assertEquals(0.63583, result[0], 0.001);
|
||||
assertEquals(0.63583, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.latLonCornerDegs(zero[0], zero[1], 100, null, false, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
// 00°38′09″N, 000°38′09″E
|
||||
assertEquals(-0.63583, result[0], 0.001);
|
||||
assertEquals(-0.63583, result[1], 0.001);
|
||||
|
||||
//test some edge cases
|
||||
//89°16′02″N, 060°12′35″E
|
||||
result = DistanceUtils.latLonCornerDegs(89.0, 0, 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(89.26722, result[0], 0.001);
|
||||
assertEquals(60.20972, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.latLonCornerDegs(0, -179.0, 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(0.63583, result[0], 0.001);
|
||||
assertEquals(-178.36417, result[1], 0.001);
|
||||
|
||||
}
|
||||
|
||||
public void testPointBearing() throws Exception {
|
||||
double[] zero = new double[]{0, 0};
|
||||
double[] zero45 = new double[]{40 * DistanceUtils.DEGREES_TO_RADIANS, DistanceUtils.DEG_45_AS_RADS};
|
||||
double[] result;
|
||||
// 00°38′09″N, 000°38′09″E
|
||||
//Verify at http://www.movable-type.co.uk/scripts/latlong.html
|
||||
result = DistanceUtils.pointOnBearing(zero[0], zero[1], 100, DistanceUtils.DEG_45_AS_RADS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
//should be above the current point at 0.8994°,0.0000°
|
||||
result = DistanceUtils.pointOnBearing(zero[0], zero[1], 100, 0, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(0.8994 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(0, result[1], 0.001);
|
||||
//directly below
|
||||
result = DistanceUtils.pointOnBearing(zero[0], zero[1], 100, DistanceUtils.DEG_180_AS_RADS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(-0.8994 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(0, result[1], 0.001);
|
||||
//0.7183°,0.5414° -- 37 deg bearing
|
||||
result = DistanceUtils.pointOnBearing(zero[0], zero[1], 100, 37 * DistanceUtils.DEGREES_TO_RADIANS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(0.7183 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(0.5414 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.pointOnBearing(zero45[0], zero45[1], 100, DistanceUtils.DEG_45_AS_RADS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
//40.6328°,45.8381°
|
||||
assertEquals(40.6328 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(45.8381 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.pointOnBearing(1 * DistanceUtils.DEGREES_TO_RADIANS, 1 * DistanceUtils.DEGREES_TO_RADIANS, 100, DistanceUtils.DEG_90_AS_RADS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
//0.9997°,1.8994°
|
||||
assertEquals(0.9997 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(1.8994 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.pointOnBearing(-10 * DistanceUtils.DEGREES_TO_RADIANS, -150 * DistanceUtils.DEGREES_TO_RADIANS, 15, 205*DistanceUtils.DEGREES_TO_RADIANS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
//-10.1222°,-150.0578°
|
||||
assertEquals(-10.1222 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(-150.0578 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.pointOnBearing(-10 * DistanceUtils.DEGREES_TO_RADIANS, -150 * DistanceUtils.DEGREES_TO_RADIANS, 200, 63*DistanceUtils.DEGREES_TO_RADIANS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
//-9.1797°,-148.3767°
|
||||
assertEquals(-9.1797 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(-148.3767 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
result = DistanceUtils.pointOnBearing(-10 * DistanceUtils.DEGREES_TO_RADIANS, -150 * DistanceUtils.DEGREES_TO_RADIANS, 3000, 63*DistanceUtils.DEGREES_TO_RADIANS, null, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
//2.7561°,-126.1281°
|
||||
assertEquals(2.7561 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
|
||||
assertEquals(-126.1281 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
|
||||
|
||||
}
|
||||
|
||||
public void testVectorDistance() throws Exception {
|
||||
double[] zero = new double[]{0, 0};
|
||||
|
||||
double[] zeroOne = new double[]{0, 1};
|
||||
double[] oneZero = new double[]{1, 0};
|
||||
double[] oneOne = new double[]{1, 1};
|
||||
double distance;
|
||||
distance = DistanceUtils.vectorDistance(zero, zeroOne, 2);
|
||||
assertEquals(1.0, distance, 0);
|
||||
distance = DistanceUtils.vectorDistance(zero, oneZero, 2);
|
||||
assertEquals(1.0, distance, 0);
|
||||
distance = DistanceUtils.vectorDistance(zero, oneOne, 2);
|
||||
assertEquals(Math.sqrt(2), distance, 0.001);
|
||||
|
||||
distance = DistanceUtils.squaredEuclideanDistance(zero, oneOne);
|
||||
assertEquals(2, distance, 0.001);
|
||||
}
|
||||
|
||||
public void testHaversine() throws Exception {
|
||||
double distance;
|
||||
//compare to http://www.movable-type.co.uk/scripts/latlong.html
|
||||
distance = DistanceUtils.haversine(0, 0, Math.PI / 4.0, Math.PI / 4.0, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(6672.0, distance, 0.5);
|
||||
|
||||
distance = DistanceUtils.haversine(0, 0, Math.toRadians(20), Math.toRadians(20), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(3112, distance, 0.5);
|
||||
|
||||
distance = DistanceUtils.haversine(0, 0, Math.toRadians(1), Math.toRadians(1), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(157.2, distance, 0.5);
|
||||
|
||||
//Try some around stuff
|
||||
distance = DistanceUtils.haversine(Math.toRadians(1), Math.toRadians(-1),
|
||||
Math.toRadians(1), Math.toRadians(1), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(222.4, distance, 0.5);
|
||||
|
||||
distance = DistanceUtils.haversine(Math.toRadians(89), Math.toRadians(-1),
|
||||
Math.toRadians(89), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(222.4, distance, 0.5);
|
||||
|
||||
distance = DistanceUtils.haversine(Math.toRadians(89), Math.toRadians(-1),
|
||||
Math.toRadians(49), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(4670, distance, 0.5);
|
||||
|
||||
distance = DistanceUtils.haversine(Math.toRadians(0), Math.toRadians(-179),
|
||||
Math.toRadians(0), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
assertEquals(222.4, distance, 0.5);
|
||||
|
||||
}
|
||||
|
||||
public void testParse() throws Exception {
|
||||
String[] parse;
|
||||
parse = DistanceUtils.parsePoint(null, "89.0,73.2", 2);
|
||||
assertEquals(2, parse.length);
|
||||
assertEquals("89.0", parse[0]);
|
||||
assertEquals("73.2", parse[1]);
|
||||
|
||||
parse = DistanceUtils.parsePoint(null, "89.0,73.2,-92.3", 3);
|
||||
assertEquals(3, parse.length);
|
||||
assertEquals("89.0", parse[0]);
|
||||
assertEquals("73.2", parse[1]);
|
||||
assertEquals("-92.3", parse[2]);
|
||||
|
||||
parse = DistanceUtils.parsePoint(null, " 89.0 , 73.2 , -92.3 ", 3);
|
||||
assertEquals(3, parse.length);
|
||||
assertEquals("89.0", parse[0]);
|
||||
assertEquals("73.2", parse[1]);
|
||||
assertEquals("-92.3", parse[2]);
|
||||
|
||||
|
||||
String[] foo = DistanceUtils.parsePoint(parse, "89.0 , 73.2 , -92.3", 3);
|
||||
//should be same piece of memory
|
||||
assertTrue(foo == parse);
|
||||
assertEquals(3, parse.length);
|
||||
assertEquals("89.0", parse[0]);
|
||||
assertEquals("73.2", parse[1]);
|
||||
assertEquals("-92.3", parse[2]);
|
||||
//array should get automatically resized
|
||||
parse = DistanceUtils.parsePoint(new String[1], "89.0 , 73.2 , -92.3", 3);
|
||||
assertEquals(3, parse.length);
|
||||
assertEquals("89.0", parse[0]);
|
||||
assertEquals("73.2", parse[1]);
|
||||
assertEquals("-92.3", parse[2]);
|
||||
|
||||
|
||||
try {
|
||||
parse = DistanceUtils.parsePoint(null, "89.0 , ", 3);
|
||||
assertTrue(false);
|
||||
} catch (InvalidGeoException e) {
|
||||
}
|
||||
try {
|
||||
parse = DistanceUtils.parsePoint(null, " , 89.0 ", 3);
|
||||
assertTrue(false);
|
||||
} catch (InvalidGeoException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
parse = DistanceUtils.parsePoint(null, "", 3);
|
||||
assertTrue(false);
|
||||
} catch (InvalidGeoException e) {
|
||||
}
|
||||
|
||||
|
||||
double[] dbls = DistanceUtils.parsePointDouble(null, "89.0 , 73.2 , -92.3", 3);
|
||||
assertEquals(3, dbls.length);
|
||||
assertEquals(89.0, dbls[0], 0);
|
||||
assertEquals(73.2, dbls[1], 0.1);
|
||||
assertEquals(-92.3, dbls[2], 0.1);
|
||||
|
||||
try {
|
||||
dbls = DistanceUtils.parsePointDouble(null, "89.0 , foo , -92.3", 3);
|
||||
assertTrue(false);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
|
||||
dbls = DistanceUtils.parseLatitudeLongitude(null, "89.0 , 73.2 ");
|
||||
assertEquals(2, dbls.length);
|
||||
assertEquals(89.0, dbls[0], 0.1);
|
||||
assertEquals(73.2, dbls[1], 0.1);
|
||||
|
||||
//test some bad lat/long pairs
|
||||
try {
|
||||
dbls = DistanceUtils.parseLatitudeLongitude(null, "189.0 , 73.2 ");
|
||||
assertTrue(false);
|
||||
} catch (InvalidGeoException e) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
dbls = DistanceUtils.parseLatitudeLongitude(null, "89.0 , 273.2 ");
|
||||
assertTrue(false);
|
||||
} catch (InvalidGeoException e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geohash;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link GeoHashUtils}
|
||||
*/
|
||||
public class TestGeoHashUtils extends LuceneTestCase {
|
||||
|
||||
/**
|
||||
* Pass condition: lat=42.6, lng=-5.6 should be encoded as "ezs42e44yx96",
|
||||
* lat=57.64911 lng=10.40744 should be encoded as "u4pruydqqvj8"
|
||||
*/
|
||||
@Test
|
||||
public void testEncode() {
|
||||
String hash = GeoHashUtils.encode(42.6, -5.6);
|
||||
assertEquals("ezs42e44yx96", hash);
|
||||
|
||||
hash = GeoHashUtils.encode(57.64911, 10.40744);
|
||||
assertEquals("u4pruydqqvj8", hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass condition: lat=52.3738007, lng=4.8909347 should be encoded and then
|
||||
* decoded within 0.00001 of the original value
|
||||
*/
|
||||
@Test
|
||||
public void testDecodePreciseLongitudeLatitude() {
|
||||
String hash = GeoHashUtils.encode(52.3738007, 4.8909347);
|
||||
|
||||
double[] latitudeLongitude = GeoHashUtils.decode(hash);
|
||||
|
||||
assertEquals(52.3738007, latitudeLongitude[0], 0.00001D);
|
||||
assertEquals(4.8909347, latitudeLongitude[1], 0.00001D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass condition: lat=84.6, lng=10.5 should be encoded and then decoded
|
||||
* within 0.00001 of the original value
|
||||
*/
|
||||
@Test
|
||||
public void testDecodeImpreciseLongitudeLatitude() {
|
||||
String hash = GeoHashUtils.encode(84.6, 10.5);
|
||||
|
||||
double[] latitudeLongitude = GeoHashUtils.decode(hash);
|
||||
|
||||
assertEquals(84.6, latitudeLongitude[0], 0.00001D);
|
||||
assertEquals(10.5, latitudeLongitude[1], 0.00001D);
|
||||
}
|
||||
|
||||
/*
|
||||
* see https://issues.apache.org/jira/browse/LUCENE-1815 for details
|
||||
*/
|
||||
@Test
|
||||
public void testDecodeEncode() {
|
||||
String geoHash = "u173zq37x014";
|
||||
assertEquals(geoHash, GeoHashUtils.encode(52.3738007, 4.8909347));
|
||||
double[] decode = GeoHashUtils.decode(geoHash);
|
||||
assertEquals(52.37380061d, decode[0], 0.000001d);
|
||||
assertEquals(4.8909343d, decode[1], 0.000001d);
|
||||
|
||||
assertEquals(geoHash, GeoHashUtils.encode(decode[0], decode[1]));
|
||||
|
||||
geoHash = "u173";
|
||||
decode = GeoHashUtils.decode("u173");
|
||||
geoHash = GeoHashUtils.encode(decode[0], decode[1]);
|
||||
assertEquals(decode[0], GeoHashUtils.decode(geoHash)[0], 0.000001d);
|
||||
assertEquals(decode[1], GeoHashUtils.decode(geoHash)[1], 0.000001d);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package org.apache.lucene.spatial.geometry;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.apache.lucene.spatial.geometry.DistanceUnits}
|
||||
*/
|
||||
public class TestDistanceUnits extends LuceneTestCase {
|
||||
|
||||
/**
|
||||
* Pass condition: When finding the DistanceUnit for "km", KILOMETRES is found. When finding the DistanceUnit for
|
||||
* "miles", MILES is found.
|
||||
*/
|
||||
@Test
|
||||
public void testFindDistanceUnit() {
|
||||
assertEquals(DistanceUnits.KILOMETERS, DistanceUnits.findDistanceUnit("km"));
|
||||
assertEquals(DistanceUnits.MILES, DistanceUnits.findDistanceUnit("miles"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass condition: Searching for the DistanceUnit of an unknown unit "mls" should throw an IllegalArgumentException.
|
||||
*/
|
||||
@Test
|
||||
public void testFindDistanceUnit_unknownUnit() {
|
||||
try {
|
||||
DistanceUnits.findDistanceUnit("mls");
|
||||
assertTrue("IllegalArgumentException should have been thrown", false);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass condition: Converting between the same units should not change the value. Converting from MILES to KILOMETRES
|
||||
* involves multiplying the distance by the ratio, and converting from KILOMETRES to MILES involves dividing by the ratio
|
||||
*/
|
||||
@Test
|
||||
public void testConvert() {
|
||||
assertEquals(10.5, DistanceUnits.MILES.convert(10.5, DistanceUnits.MILES), 0D);
|
||||
assertEquals(10.5, DistanceUnits.KILOMETERS.convert(10.5, DistanceUnits.KILOMETERS), 0D);
|
||||
assertEquals(10.5 * 1.609344, DistanceUnits.KILOMETERS.convert(10.5, DistanceUnits.MILES), 0D);
|
||||
assertEquals(10.5 / 1.609344, DistanceUnits.MILES.convert(10.5, DistanceUnits.KILOMETERS), 0D);
|
||||
}
|
||||
}
|
|
@ -1,493 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.FieldType.NumericType;
|
||||
import org.apache.lucene.document.DoubleField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.FieldInfo.IndexOptions;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.spatial.DistanceUtils;
|
||||
import org.apache.lucene.spatial.geohash.GeoHashUtils;
|
||||
import org.apache.lucene.spatial.geometry.DistanceUnits;
|
||||
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
||||
import org.apache.lucene.spatial.geometry.LatLng;
|
||||
import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
|
||||
import org.apache.lucene.spatial.tier.projections.IProjector;
|
||||
import org.apache.lucene.spatial.tier.projections.SinusoidalProjector;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
public class TestCartesian extends LuceneTestCase {
|
||||
|
||||
private Directory directory;
|
||||
private IndexSearcher searcher;
|
||||
// reston va
|
||||
private double lat = 38.969398;
|
||||
private double lng= -77.386398;
|
||||
private String latField = "lat";
|
||||
private String lngField = "lng";
|
||||
private List<CartesianTierPlotter> ctps = new LinkedList<CartesianTierPlotter>();
|
||||
private String geoHashPrefix = "_geoHash_";
|
||||
|
||||
private IProjector project = new SinusoidalProjector();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
directory = newDirectory();
|
||||
|
||||
IndexWriter writer = new IndexWriter(directory, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
|
||||
|
||||
setUpPlotter( 2, 15);
|
||||
|
||||
addData(writer);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
directory.close();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
private void setUpPlotter(int base, int top) {
|
||||
|
||||
for (; base <= top; base ++){
|
||||
ctps.add(new CartesianTierPlotter(base,project,
|
||||
CartesianTierPlotter.DEFALT_FIELD_PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
private static final FieldType latLongType = new FieldType();
|
||||
static {
|
||||
latLongType.setIndexed(true);
|
||||
latLongType.setStored(true);
|
||||
latLongType.setTokenized(true);
|
||||
latLongType.setOmitNorms(true);
|
||||
latLongType.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
latLongType.setNumericType(NumericType.DOUBLE);
|
||||
latLongType.setNumericPrecisionStep(Integer.MAX_VALUE);
|
||||
latLongType.freeze();
|
||||
}
|
||||
|
||||
private void addPoint(IndexWriter writer, String name, double lat, double lng) throws IOException{
|
||||
|
||||
Document doc = new Document();
|
||||
|
||||
doc.add(newField("name", name, TextField.TYPE_STORED));
|
||||
|
||||
// convert the lat / long to lucene fields
|
||||
doc.add(new DoubleField(latField, lat, latLongType));
|
||||
doc.add(new DoubleField(lngField, lng, latLongType));
|
||||
|
||||
// add a default meta field to make searching all documents easy
|
||||
doc.add(newField("metafile", "doc", TextField.TYPE_STORED));
|
||||
|
||||
int ctpsize = ctps.size();
|
||||
for (int i =0; i < ctpsize; i++){
|
||||
CartesianTierPlotter ctp = ctps.get(i);
|
||||
doc.add(new DoubleField(ctp.getTierFieldName(), ctp.getTierBoxId(lat, lng), latLongType));
|
||||
|
||||
doc.add(newField(geoHashPrefix, GeoHashUtils.encode(lat,lng), StringField.TYPE_STORED));
|
||||
}
|
||||
writer.addDocument(doc);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void addData(IndexWriter writer) throws IOException {
|
||||
addPoint(writer,"McCormick & Schmick's Seafood Restaurant",38.9579000,-77.3572000);
|
||||
addPoint(writer,"Jimmy's Old Town Tavern",38.9690000,-77.3862000);
|
||||
addPoint(writer,"Ned Devine's",38.9510000,-77.4107000);
|
||||
addPoint(writer,"Old Brogue Irish Pub",38.9955000,-77.2884000);
|
||||
addPoint(writer,"Alf Laylah Wa Laylah",38.8956000,-77.4258000);
|
||||
addPoint(writer,"Sully's Restaurant & Supper",38.9003000,-77.4467000);
|
||||
addPoint(writer,"TGIFriday",38.8725000,-77.3829000);
|
||||
addPoint(writer,"Potomac Swing Dance Club",38.9027000,-77.2639000);
|
||||
addPoint(writer,"White Tiger Restaurant",38.9027000,-77.2638000);
|
||||
addPoint(writer,"Jammin' Java",38.9039000,-77.2622000);
|
||||
addPoint(writer,"Potomac Swing Dance Club",38.9027000,-77.2639000);
|
||||
addPoint(writer,"WiseAcres Comedy Club",38.9248000,-77.2344000);
|
||||
addPoint(writer,"Glen Echo Spanish Ballroom",38.9691000,-77.1400000);
|
||||
addPoint(writer,"Whitlow's on Wilson",38.8889000,-77.0926000);
|
||||
addPoint(writer,"Iota Club and Cafe",38.8890000,-77.0923000);
|
||||
addPoint(writer,"Hilton Washington Embassy Row",38.9103000,-77.0451000);
|
||||
addPoint(writer,"HorseFeathers, Bar & Grill", 39.01220000000001, -77.3942);
|
||||
addPoint(writer,"Marshall Island Airfield",7.06, 171.2);
|
||||
addPoint(writer, "Wonga Wongue Reserve, Gabon", -0.546562,9.459229);
|
||||
addPoint(writer,"Midway Island",25.7, -171.7);
|
||||
addPoint(writer,"North Pole Way",55.0, 4.0);
|
||||
|
||||
writer.commit();
|
||||
// TODO: fix CustomScoreQuery usage in testRange/testGeoHashRange so we don't need this.
|
||||
writer.forceMerge(1);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
|
||||
public void testDistances() throws IOException, InvalidGeoException {
|
||||
LatLng p1 = new FloatLatLng( 7.06, 171.2 );
|
||||
LatLng p2 = new FloatLatLng( 21.6032207, -158.0 );
|
||||
double miles = p1.arcDistance( p2, DistanceUnits.MILES );
|
||||
if (VERBOSE) {
|
||||
System.out.println("testDistances");
|
||||
System.out.println("miles:" + miles);
|
||||
}
|
||||
assertEquals(2288.82495932794, miles, 0.001);
|
||||
LatLng p3 = new FloatLatLng( 41.6032207, -73.087749);
|
||||
LatLng p4 = new FloatLatLng( 55.0, 4.0 );
|
||||
miles = p3.arcDistance( p4, DistanceUnits.MILES );
|
||||
if (VERBOSE) System.out.println("miles:" + miles);
|
||||
assertEquals(3474.331719997617, miles, 0.001);
|
||||
}
|
||||
|
||||
/*public void testCartesianPolyFilterBuilder() throws Exception {
|
||||
CartesianPolyFilterBuilder cpfb = new CartesianPolyFilterBuilder(CartesianTierPlotter.DEFALT_FIELD_PREFIX, 2, 15);
|
||||
//try out some shapes
|
||||
final double miles = 20.0;
|
||||
// Hawaii
|
||||
// 2300 miles to Marshall Island Airfield
|
||||
//Hawaii to Midway is 911 miles
|
||||
lat = 0;
|
||||
lng = -179.9;
|
||||
Shape shape;
|
||||
shape = cpfb.getBoxShape(lat, lng, miles);
|
||||
System.out.println("Tier: " + shape.getTierLevel());
|
||||
System.out.println("area: " + shape.getArea().size());
|
||||
lat = 30;
|
||||
lng = -100;
|
||||
shape = cpfb.getBoxShape(lat, lng, miles);
|
||||
System.out.println("Tier: " + shape.getTierLevel());
|
||||
System.out.println("area: " + shape.getArea().size());
|
||||
|
||||
lat = 30;
|
||||
lng = 100;
|
||||
shape = cpfb.getBoxShape(lat, lng, miles);
|
||||
System.out.println("Tier: " + shape.getTierLevel());
|
||||
System.out.println("area: " + shape.getArea().size());
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public void testAntiM() throws IOException, InvalidGeoException {
|
||||
IndexReader reader = IndexReader.open(directory);
|
||||
searcher = new IndexSearcher(reader);
|
||||
|
||||
final double miles = 2800.0;
|
||||
// Hawaii
|
||||
// 2300 miles to Marshall Island Airfield
|
||||
//Hawaii to Midway is 911 miles
|
||||
lat = 21.6032207;
|
||||
lng = -158.0;
|
||||
|
||||
if (VERBOSE) System.out.println("testAntiM");
|
||||
// create a distance query
|
||||
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
|
||||
latField, lngField, CartesianTierPlotter.DEFALT_FIELD_PREFIX, true, 2, 15);
|
||||
|
||||
if (VERBOSE) System.out.println(dq);
|
||||
//create a term query to search against all documents
|
||||
Query tq = new TermQuery(new Term("metafile", "doc"));
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
//
|
||||
DistanceFieldComparatorSource dsort = new DistanceFieldComparatorSource(dq.distanceFilter);
|
||||
Sort sort = new Sort(new SortField("foo", dsort,false));
|
||||
|
||||
// Perform the search, using the term query, the serial chain filter, and the
|
||||
// distance sort
|
||||
TopDocs hits = searcher.search(dq.getQuery(tq),null, 1000, sort);
|
||||
int results = hits.totalHits;
|
||||
ScoreDoc[] scoreDocs = hits.scoreDocs;
|
||||
|
||||
// Get a list of distances
|
||||
Map<Integer,Double> distances = dq.distanceFilter.getDistances();
|
||||
|
||||
// distances calculated from filter first pass must be less than total
|
||||
// docs, from the above test of 20 items, 12 will come from the boundary box
|
||||
// filter, but only 5 are actually in the radius of the results.
|
||||
|
||||
// Note Boundary Box filtering, is not accurate enough for most systems.
|
||||
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Distance Filter filtered: " + distances.size());
|
||||
System.out.println("Results: " + results);
|
||||
System.out.println("=============================");
|
||||
System.out.println("Distances should be 2 "+ distances.size());
|
||||
System.out.println("Results should be 2 "+ results);
|
||||
}
|
||||
|
||||
assertEquals(2, distances.size()); // fixed a store of only needed distances
|
||||
assertEquals(2, results);
|
||||
double lastDistance = 0;
|
||||
for(int i =0 ; i < results; i++){
|
||||
Document d = searcher.doc(scoreDocs[i].doc);
|
||||
|
||||
String name = d.get("name");
|
||||
double rsLat = d.getField(latField).numericValue().doubleValue();
|
||||
double rsLng = d.getField(lngField).numericValue().doubleValue();
|
||||
Double geo_distance = distances.get(scoreDocs[i].doc);
|
||||
|
||||
double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
|
||||
double llm = DistanceUtils.getLLMDistance(lat, lng, rsLat, rsLng);
|
||||
if (VERBOSE) System.out.println("Name: "+ name +", Distance "+ distance); //(res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ hits.score(i));
|
||||
assertTrue(Math.abs((distance - llm)) < 1);
|
||||
assertTrue((distance < miles ));
|
||||
assertTrue(geo_distance >= lastDistance);
|
||||
lastDistance = geo_distance;
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
|
||||
public void testPoleFlipping() throws IOException, InvalidGeoException {
|
||||
IndexReader reader = IndexReader.open(directory);
|
||||
searcher = new IndexSearcher(reader);
|
||||
|
||||
final double miles = 3500.0;
|
||||
lat = 41.6032207;
|
||||
lng = -73.087749;
|
||||
|
||||
if (VERBOSE) System.out.println("testPoleFlipping");
|
||||
|
||||
// create a distance query
|
||||
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
|
||||
latField, lngField, CartesianTierPlotter.DEFALT_FIELD_PREFIX, true, 2, 15);
|
||||
|
||||
if (VERBOSE) System.out.println(dq);
|
||||
//create a term query to search against all documents
|
||||
Query tq = new TermQuery(new Term("metafile", "doc"));
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
//
|
||||
DistanceFieldComparatorSource dsort = new DistanceFieldComparatorSource(dq.distanceFilter);
|
||||
Sort sort = new Sort(new SortField("foo", dsort,false));
|
||||
|
||||
// Perform the search, using the term query, the serial chain filter, and the
|
||||
// distance sort
|
||||
TopDocs hits = searcher.search(dq.getQuery(tq),null, 1000, sort);
|
||||
int results = hits.totalHits;
|
||||
ScoreDoc[] scoreDocs = hits.scoreDocs;
|
||||
|
||||
// Get a list of distances
|
||||
Map<Integer,Double> distances = dq.distanceFilter.getDistances();
|
||||
|
||||
// distances calculated from filter first pass must be less than total
|
||||
// docs, from the above test of 20 items, 12 will come from the boundary box
|
||||
// filter, but only 5 are actually in the radius of the results.
|
||||
|
||||
// Note Boundary Box filtering, is not accurate enough for most systems.
|
||||
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Distance Filter filtered: " + distances.size());
|
||||
System.out.println("Results: " + results);
|
||||
System.out.println("=============================");
|
||||
System.out.println("Distances should be 18 "+ distances.size());
|
||||
System.out.println("Results should be 18 "+ results);
|
||||
}
|
||||
|
||||
assertEquals(18, distances.size()); // fixed a store of only needed distances
|
||||
assertEquals(18, results);
|
||||
double lastDistance = 0;
|
||||
for(int i =0 ; i < results; i++){
|
||||
Document d = searcher.doc(scoreDocs[i].doc);
|
||||
String name = d.get("name");
|
||||
double rsLat = d.getField(latField).numericValue().doubleValue();
|
||||
double rsLng = d.getField(lngField).numericValue().doubleValue();
|
||||
Double geo_distance = distances.get(scoreDocs[i].doc);
|
||||
|
||||
double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
|
||||
double llm = DistanceUtils.getLLMDistance(lat, lng, rsLat, rsLng);
|
||||
if (VERBOSE) System.out.println("Name: "+ name +", Distance "+ distance); //(res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ hits.score(i));
|
||||
assertTrue(Math.abs((distance - llm)) < 1);
|
||||
if (VERBOSE) System.out.println("checking limit "+ distance + " < " + miles);
|
||||
assertTrue((distance < miles ));
|
||||
if (VERBOSE) System.out.println("checking sort "+ geo_distance + " >= " + lastDistance);
|
||||
assertTrue(geo_distance >= lastDistance);
|
||||
lastDistance = geo_distance;
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
|
||||
public void testRange() throws IOException, InvalidGeoException {
|
||||
IndexReader reader = IndexReader.open(directory);
|
||||
searcher = new IndexSearcher(reader);
|
||||
|
||||
final double[] milesToTest = new double[] {6.0, 0.5, 0.001, 0.0};
|
||||
final int[] expected = new int[] {7, 1, 0, 0};
|
||||
|
||||
for(int x=0;x<expected.length;x++) {
|
||||
|
||||
final double miles = milesToTest[x];
|
||||
|
||||
// create a distance query
|
||||
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
|
||||
latField, lngField, CartesianTierPlotter.DEFALT_FIELD_PREFIX, true, 2, 15);
|
||||
|
||||
if (VERBOSE) System.out.println(dq);
|
||||
//create a term query to search against all documents
|
||||
Query tq = new TermQuery(new Term("metafile", "doc"));
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
//
|
||||
DistanceFieldComparatorSource dsort = new DistanceFieldComparatorSource(dq.distanceFilter);
|
||||
Sort sort = new Sort(new SortField("foo", dsort,false));
|
||||
|
||||
// Perform the search, using the term query, the serial chain filter, and the
|
||||
// distance sort
|
||||
TopDocs hits = searcher.search(dq.getQuery(tq),null, 1000, sort);
|
||||
int results = hits.totalHits;
|
||||
ScoreDoc[] scoreDocs = hits.scoreDocs;
|
||||
|
||||
// Get a list of distances
|
||||
Map<Integer,Double> distances = dq.distanceFilter.getDistances();
|
||||
|
||||
// distances calculated from filter first pass must be less than total
|
||||
// docs, from the above test of 20 items, 12 will come from the boundary box
|
||||
// filter, but only 5 are actually in the radius of the results.
|
||||
|
||||
// Note Boundary Box filtering, is not accurate enough for most systems.
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Distance Filter filtered: " + distances.size());
|
||||
System.out.println("Results: " + results);
|
||||
System.out.println("=============================");
|
||||
System.out.println("Distances should be 7 "+ expected[x] + ":" + distances.size());
|
||||
System.out.println("Results should be 7 "+ expected[x] + ":" + results);
|
||||
}
|
||||
|
||||
assertEquals(expected[x], distances.size()); // fixed a store of only needed distances
|
||||
assertEquals(expected[x], results);
|
||||
double lastDistance = 0;
|
||||
for(int i =0 ; i < results; i++){
|
||||
Document d = searcher.doc(scoreDocs[i].doc);
|
||||
|
||||
String name = d.get("name");
|
||||
double rsLat = d.getField(latField).numericValue().doubleValue();
|
||||
double rsLng = d.getField(lngField).numericValue().doubleValue();
|
||||
Double geo_distance = distances.get(scoreDocs[i].doc);
|
||||
|
||||
double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
|
||||
double llm = DistanceUtils.getLLMDistance(lat, lng, rsLat, rsLng);
|
||||
if (VERBOSE) System.out.println("Name: "+ name +", Distance "+ distance); //(res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ hits.score(i));
|
||||
assertTrue(Math.abs((distance - llm)) < 1);
|
||||
assertTrue((distance < miles ));
|
||||
assertTrue(geo_distance > lastDistance);
|
||||
lastDistance = geo_distance;
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void testGeoHashRange() throws IOException, InvalidGeoException {
|
||||
IndexReader reader = IndexReader.open(directory);
|
||||
searcher = new IndexSearcher(reader);
|
||||
|
||||
final double[] milesToTest = new double[] {6.0, 0.5, 0.001, 0.0};
|
||||
final int[] expected = new int[] {7, 1, 0, 0};
|
||||
|
||||
for(int x=0;x<expected.length;x++) {
|
||||
final double miles = milesToTest[x];
|
||||
|
||||
// create a distance query
|
||||
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
|
||||
geoHashPrefix, CartesianTierPlotter.DEFALT_FIELD_PREFIX, true, 2, 15);
|
||||
|
||||
if (VERBOSE) System.out.println(dq);
|
||||
//create a term query to search against all documents
|
||||
Query tq = new TermQuery(new Term("metafile", "doc"));
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
//
|
||||
//DistanceFieldComparatorSource dsort = new DistanceFieldComparatorSource(dq.distanceFilter);
|
||||
//Sort sort = new Sort(new SortField("foo", dsort));
|
||||
|
||||
// Perform the search, using the term query, the serial chain filter, and the
|
||||
// distance sort
|
||||
TopDocs hits = searcher.search(tq,dq.getFilter(), 1000); //,sort);
|
||||
int results = hits.totalHits;
|
||||
ScoreDoc[] scoreDocs = hits.scoreDocs;
|
||||
|
||||
// Get a list of distances
|
||||
Map<Integer,Double> distances = dq.distanceFilter.getDistances();
|
||||
|
||||
// distances calculated from filter first pass must be less than total
|
||||
// docs, from the above test of 20 items, 12 will come from the boundary box
|
||||
// filter, but only 5 are actually in the radius of the results.
|
||||
|
||||
// Note Boundary Box filtering, is not accurate enough for most systems.
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Distance Filter filtered: " + distances.size());
|
||||
System.out.println("Results: " + results);
|
||||
System.out.println("=============================");
|
||||
System.out.println("Distances should be 14 "+ expected[x] + ":" + distances.size());
|
||||
System.out.println("Results should be 7 "+ expected[x] + ":" + results);
|
||||
}
|
||||
|
||||
assertEquals(expected[x], distances.size());
|
||||
assertEquals(expected[x], results);
|
||||
|
||||
for(int i =0 ; i < results; i++){
|
||||
Document d = searcher.doc(scoreDocs[i].doc);
|
||||
|
||||
String name = d.get("name");
|
||||
double rsLat = d.getField(latField).numericValue().doubleValue();
|
||||
double rsLng = d.getField(lngField).numericValue().doubleValue();
|
||||
Double geo_distance = distances.get(scoreDocs[i].doc);
|
||||
|
||||
double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
|
||||
double llm = DistanceUtils.getLLMDistance(lat, lng, rsLat, rsLng);
|
||||
if (VERBOSE) System.out.println("Name: "+ name +", Distance (res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ scoreDocs[i].score);
|
||||
assertTrue(Math.abs((distance - llm)) < 1);
|
||||
assertTrue((distance < miles ));
|
||||
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.tier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.FieldType.NumericType;
|
||||
import org.apache.lucene.document.DoubleField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.index.FieldInfo.IndexOptions;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.QueryWrapperFilter;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
|
||||
public class TestDistance extends LuceneTestCase {
|
||||
|
||||
private Directory directory;
|
||||
// reston va
|
||||
private double lat = 38.969398;
|
||||
private double lng= -77.386398;
|
||||
private String latField = "lat";
|
||||
private String lngField = "lng";
|
||||
private IndexWriter writer;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
directory = newDirectory();
|
||||
writer = new IndexWriter(directory, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
|
||||
addData(writer);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
writer.close();
|
||||
directory.close();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
private static final FieldType latLongType = new FieldType();
|
||||
static {
|
||||
latLongType.setIndexed(true);
|
||||
latLongType.setStored(true);
|
||||
latLongType.setTokenized(true);
|
||||
latLongType.setOmitNorms(true);
|
||||
latLongType.setIndexOptions(IndexOptions.DOCS_ONLY);
|
||||
latLongType.setNumericType(NumericType.DOUBLE);
|
||||
latLongType.setNumericPrecisionStep(Integer.MAX_VALUE);
|
||||
latLongType.freeze();
|
||||
}
|
||||
|
||||
private void addPoint(IndexWriter writer, String name, double lat, double lng) throws IOException{
|
||||
|
||||
Document doc = new Document();
|
||||
|
||||
doc.add(newField("name", name, TextField.TYPE_STORED));
|
||||
|
||||
// convert the lat / long to lucene fields
|
||||
doc.add(new DoubleField(latField, lat, latLongType));
|
||||
doc.add(new DoubleField(lngField, lng, latLongType));
|
||||
|
||||
// add a default meta field to make searching all documents easy
|
||||
doc.add(newField("metafile", "doc", TextField.TYPE_STORED));
|
||||
writer.addDocument(doc);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void addData(IndexWriter writer) throws IOException {
|
||||
addPoint(writer,"McCormick & Schmick's Seafood Restaurant",38.9579000,-77.3572000);
|
||||
addPoint(writer,"Jimmy's Old Town Tavern",38.9690000,-77.3862000);
|
||||
addPoint(writer,"Ned Devine's",38.9510000,-77.4107000);
|
||||
addPoint(writer,"Old Brogue Irish Pub",38.9955000,-77.2884000);
|
||||
addPoint(writer,"Alf Laylah Wa Laylah",38.8956000,-77.4258000);
|
||||
addPoint(writer,"Sully's Restaurant & Supper",38.9003000,-77.4467000);
|
||||
addPoint(writer,"TGIFriday",38.8725000,-77.3829000);
|
||||
addPoint(writer,"Potomac Swing Dance Club",38.9027000,-77.2639000);
|
||||
addPoint(writer,"White Tiger Restaurant",38.9027000,-77.2638000);
|
||||
addPoint(writer,"Jammin' Java",38.9039000,-77.2622000);
|
||||
addPoint(writer,"Potomac Swing Dance Club",38.9027000,-77.2639000);
|
||||
addPoint(writer,"WiseAcres Comedy Club",38.9248000,-77.2344000);
|
||||
addPoint(writer,"Glen Echo Spanish Ballroom",38.9691000,-77.1400000);
|
||||
addPoint(writer,"Whitlow's on Wilson",38.8889000,-77.0926000);
|
||||
addPoint(writer,"Iota Club and Cafe",38.8890000,-77.0923000);
|
||||
addPoint(writer,"Hilton Washington Embassy Row",38.9103000,-77.0451000);
|
||||
addPoint(writer,"HorseFeathers, Bar & Grill", 39.01220000000001, -77.3942);
|
||||
writer.commit();
|
||||
}
|
||||
|
||||
public void testLatLongFilterOnDeletedDocs() throws Exception {
|
||||
writer.deleteDocuments(new Term("name", "Potomac"));
|
||||
IndexReader r = IndexReader.open(writer, true);
|
||||
LatLongDistanceFilter f = new LatLongDistanceFilter(new QueryWrapperFilter(new MatchAllDocsQuery()),
|
||||
lat, lng, 1.0, latField, lngField);
|
||||
|
||||
AtomicReaderContext[] leaves = r.getTopReaderContext().leaves();
|
||||
for (int i = 0; i < leaves.length; i++) {
|
||||
f.getDocIdSet(leaves[i], leaves[i].reader().getLiveDocs());
|
||||
}
|
||||
r.close();
|
||||
}
|
||||
|
||||
/* these tests do not test anything, as no assertions:
|
||||
public void testMiles() {
|
||||
double LLM = DistanceUtils.getInstance().getLLMDistance(lat, lng,39.012200001, -77.3942);
|
||||
System.out.println(LLM);
|
||||
System.out.println("-->"+DistanceUtils.getInstance().getDistanceMi(lat, lng, 39.0122, -77.3942));
|
||||
}
|
||||
|
||||
public void testMiles2(){
|
||||
System.out.println("Test Miles 2");
|
||||
double LLM = DistanceUtils.getInstance().getLLMDistance(44.30073, -78.32131,43.687267, -79.39842);
|
||||
System.out.println(LLM);
|
||||
System.out.println("-->"+DistanceUtils.getInstance().getDistanceMi(44.30073, -78.32131, 43.687267, -79.39842));
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// public void testDistanceQueryCacheable() throws IOException {
|
||||
//
|
||||
// // create two of the same distance queries
|
||||
// double miles = 6.0;
|
||||
// DistanceQuery dq1 = new DistanceQuery(lat, lng, miles, latField, lngField, true);
|
||||
// DistanceQuery dq2 = new DistanceQuery(lat, lng, miles, latField, lngField, true);
|
||||
//
|
||||
// /* ensure that they hash to the same code, which will cause a cache hit in solr */
|
||||
// System.out.println("hash differences?");
|
||||
// assertEquals(dq1.getQuery().hashCode(), dq2.getQuery().hashCode());
|
||||
//
|
||||
// /* ensure that changing the radius makes a different hash code, creating a cache miss in solr */
|
||||
// DistanceQuery widerQuery = new DistanceQuery(lat, lng, miles + 5.0, latField, lngField, false);
|
||||
// assertTrue(dq1.getQuery().hashCode() != widerQuery.getQuery().hashCode());
|
||||
// }
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package org.apache.lucene.spatial.tier.projections;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
public class SinusoidalProjectorTest extends LuceneTestCase {
|
||||
|
||||
@Test
|
||||
public void testProjection() throws Exception {
|
||||
SinusoidalProjector prj = new SinusoidalProjector();
|
||||
//TODO: uncomment once SinusoidalProjector is fixed. Unfortunately, fixing it breaks a lot of other stuff
|
||||
/*double[] doubles;
|
||||
doubles = prj.coords(-89.0, 10);
|
||||
assertEquals(0.003, doubles[0], 0.001);//x
|
||||
assertEquals(-89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
|
||||
|
||||
doubles = prj.coords(89.0, 0);
|
||||
assertEquals(0.0, doubles[0]);//x
|
||||
assertEquals(89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
|
||||
|
||||
doubles = prj.coords(89.0, 10);
|
||||
assertEquals(0.003, doubles[0], 0.001);//x
|
||||
assertEquals(89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
|
||||
|
||||
|
||||
doubles = prj.coords(-89.0, 0);
|
||||
assertEquals(0.0, doubles[0]);//x
|
||||
assertEquals(-89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);*/
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//This code demonstrates that the SinusoidalProjector is incorrect
|
||||
/*@Test
|
||||
public void testFoo() throws Exception {
|
||||
CartesianTierPlotter plotter = new CartesianTierPlotter(11, new SinusoidalProjector(), "foo");
|
||||
SinusoidalProjector prj = new SinusoidalProjector();
|
||||
System.out.println("---- Equator ---");
|
||||
printValues(plotter, prj, 0);
|
||||
System.out.println("---- North ---");
|
||||
printValues(plotter, prj, 89.0);
|
||||
System.out.println("---- South ---");
|
||||
printValues(plotter, prj, -89.0);
|
||||
}
|
||||
|
||||
private void printValues(CartesianTierPlotter plotter, SinusoidalProjector prj, double latitude){
|
||||
for (int i = 0; i <= 10; i++){
|
||||
double boxId = plotter.getTierBoxId(latitude, i);
|
||||
double[] doubles = prj.coords(latitude, i);
|
||||
System.out.println("Box[" + latitude + ", " + i + "] = " + boxId + " sinusoidal: [" + doubles[0] + ", " + doubles[1] + "]");
|
||||
}
|
||||
for (int i = -10; i <= 0; i++){
|
||||
double boxId = plotter.getTierBoxId(latitude, i);
|
||||
double[] doubles = prj.coords(latitude, i);
|
||||
System.out.println("Box[" + latitude + ", " + i + "] = " + boxId + " sinusoidal: [" + doubles[0] + ", " + doubles[1] + "]");
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed 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.
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0"?>
|
||||
<project name="spatial" default="default">
|
||||
<description>
|
||||
Lucene Spatial
|
||||
</description>
|
||||
|
||||
<property name="build.dir" location="build/" />
|
||||
<property name="dist.dir" location="dist/" />
|
||||
<property name="maven.dist.dir" location="../dist/maven" />
|
||||
|
||||
<path id="additional.dependencies">
|
||||
<fileset dir="lib" includes="*.jar"/>
|
||||
</path>
|
||||
|
||||
<pathconvert property="project.classpath"
|
||||
targetos="unix"
|
||||
refid="additional.dependencies"
|
||||
/>
|
||||
|
||||
<import file="../../lucene/contrib/contrib-build.xml"/>
|
||||
|
||||
<path id="classpath">
|
||||
<path refid="base.classpath"/>
|
||||
<pathelement path="${spatial-base.jar}" />
|
||||
<pathelement path="${queries.jar}" />
|
||||
</path>
|
||||
|
||||
<path id="test.classpath">
|
||||
<path refid="test.base.classpath" />
|
||||
<path refid="base.classpath"/>
|
||||
<pathelement path="${analyzers-common.jar}" />
|
||||
<pathelement path="src/test-files" />
|
||||
</path>
|
||||
|
||||
|
||||
<target name="init" depends="contrib-build.init"/>
|
||||
<target name="dist-maven" depends="jar-core,javadocs,common.dist-maven"/>
|
||||
<target name="compile" depends="jar-queries,common.compile-core" />
|
||||
<target name="test" depends="jar-analyzers-common,compile-test,validate,junit-mkdir,junit-sequential,junit-parallel" description="Runs unit tests"/>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,2 @@
|
|||
AnyObjectId[82d4eadc1a5301bb86440e1eac81834fea8cba49] was removed in git history.
|
||||
Apache SVN contains full history.
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed 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.
|
|
@ -0,0 +1,5 @@
|
|||
Apache Commons Lang
|
||||
Copyright 2001-2008 The Apache Software Foundation
|
||||
|
||||
This product includes software developed by
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
|
||||
public class SimpleSpatialFieldInfo implements SpatialFieldInfo {
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
public SimpleSpatialFieldInfo(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* 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.
|
||||
|
@ -15,16 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
* Information the strategy needs for the lucene fields
|
||||
*/
|
||||
public class InvalidGeoException extends Exception {
|
||||
|
||||
public InvalidGeoException(String message){
|
||||
super(message);
|
||||
}
|
||||
public interface SpatialFieldInfo {
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
||||
/**
|
||||
* must be thread safe
|
||||
*/
|
||||
public abstract class SpatialStrategy<T extends SpatialFieldInfo> {
|
||||
|
||||
protected boolean ignoreIncompatibleGeometry = false;
|
||||
protected final SpatialContext ctx;
|
||||
|
||||
public SpatialStrategy(SpatialContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public SpatialContext getSpatialContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** Corresponds with Solr's FieldType.isPolyField(). */
|
||||
public boolean isPolyField() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Corresponds with Solr's FieldType.createField().
|
||||
*
|
||||
* This may return a null field if it does not want to make anything.
|
||||
* This is reasonable behavior if 'ignoreIncompatibleGeometry=true' and the
|
||||
* geometry is incompatible
|
||||
*/
|
||||
public abstract IndexableField createField(T fieldInfo, Shape shape, boolean index, boolean store);
|
||||
|
||||
/** Corresponds with Solr's FieldType.createFields(). */
|
||||
public IndexableField[] createFields(T fieldInfo, Shape shape, boolean index, boolean store) {
|
||||
return new IndexableField[] { createField(fieldInfo, shape, index, store) };
|
||||
}
|
||||
|
||||
public abstract ValueSource makeValueSource(SpatialArgs args, T fieldInfo);
|
||||
|
||||
/**
|
||||
* Make a query
|
||||
*/
|
||||
public abstract Query makeQuery(SpatialArgs args, T fieldInfo);
|
||||
|
||||
/**
|
||||
* Make a Filter
|
||||
*/
|
||||
public abstract Filter makeFilter(SpatialArgs args, T fieldInfo);
|
||||
|
||||
public boolean isIgnoreIncompatibleGeometry() {
|
||||
return ignoreIncompatibleGeometry;
|
||||
}
|
||||
|
||||
public void setIgnoreIncompatibleGeometry(boolean ignoreIncompatibleGeometry) {
|
||||
this.ignoreIncompatibleGeometry = ignoreIncompatibleGeometry;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* 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.
|
||||
|
@ -15,17 +15,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
|
||||
/**
|
||||
* Lucene spatial search
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
* Check:
|
||||
* http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
|
||||
*/
|
||||
public enum IntersectCase {
|
||||
WITHIN,
|
||||
CONTAINS,
|
||||
OUTSIDE,
|
||||
INTERSECTS;
|
||||
}
|
||||
package org.apache.lucene.spatial;
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import org.apache.lucene.spatial.prefix.tree.Node;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.util.ShapeFieldCacheProvider;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
|
||||
public class PointPrefixTreeFieldCacheProvider extends ShapeFieldCacheProvider<Point> {
|
||||
|
||||
final SpatialPrefixTree grid; //
|
||||
|
||||
public PointPrefixTreeFieldCacheProvider(SpatialPrefixTree grid, String shapeField, int defaultSize) {
|
||||
super( shapeField, defaultSize );
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
//A kluge that this is a field
|
||||
private Node scanCell = null;
|
||||
|
||||
@Override
|
||||
protected Point readShape(BytesRef term) {
|
||||
scanCell = grid.getNode(term.bytes, term.offset, term.length, scanCell);
|
||||
return scanCell.isLeaf() ? scanCell.getShape().getCenter() : null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PrefixCellsTokenizer extends Tokenizer {
|
||||
public PrefixCellsTokenizer(Reader input) {
|
||||
super(input);
|
||||
}
|
||||
|
||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
||||
|
||||
@Override
|
||||
public final boolean incrementToken() throws IOException {
|
||||
clearAttributes();
|
||||
int length = 0;
|
||||
char[] buffer = termAtt.buffer();
|
||||
while (true) {
|
||||
char c = (char) input.read();
|
||||
if (c < 0) break;
|
||||
if (c == 'a' || c == 'A') {
|
||||
buffer[length++] = 'A';
|
||||
continue;
|
||||
}
|
||||
if (c == 'b' || c == 'B') {
|
||||
buffer[length++] = 'B';
|
||||
continue;
|
||||
}
|
||||
if (c == 'c' || c == 'C') {
|
||||
buffer[length++] = 'C';
|
||||
continue;
|
||||
}
|
||||
if (c == 'd' || c == 'D') {
|
||||
buffer[length++] = 'D';
|
||||
continue;
|
||||
}
|
||||
if (c == '*') {
|
||||
buffer[length++] = '*';
|
||||
continue;
|
||||
}
|
||||
if (c == '+') {
|
||||
buffer[length++] = '+';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
// Skip any other character
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
termAtt.setLength(length);
|
||||
return length > 0; // should only happen at the end
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void end() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(Reader input) throws IOException {
|
||||
super.reset(input);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.spatial.SimpleSpatialFieldInfo;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.Node;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.util.CachedDistanceValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class PrefixTreeStrategy extends SpatialStrategy<SimpleSpatialFieldInfo> {
|
||||
protected final SpatialPrefixTree grid;
|
||||
private final Map<String, PointPrefixTreeFieldCacheProvider> provider = new ConcurrentHashMap<String, PointPrefixTreeFieldCacheProvider>();
|
||||
protected int defaultFieldValuesArrayLen = 2;
|
||||
protected double distErrPct = SpatialArgs.DEFAULT_DIST_PRECISION;
|
||||
|
||||
public PrefixTreeStrategy(SpatialPrefixTree grid) {
|
||||
super(grid.getSpatialContext());
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
/** Used in the in-memory ValueSource as a default ArrayList length for this field's array of values, per doc. */
|
||||
public void setDefaultFieldValuesArrayLen(int defaultFieldValuesArrayLen) {
|
||||
this.defaultFieldValuesArrayLen = defaultFieldValuesArrayLen;
|
||||
}
|
||||
|
||||
/** See {@link SpatialPrefixTree#getMaxLevelForPrecision(com.spatial4j.core.shape.Shape, double)}. */
|
||||
public void setDistErrPct(double distErrPct) {
|
||||
this.distErrPct = distErrPct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexableField createField(SimpleSpatialFieldInfo fieldInfo, Shape shape, boolean index, boolean store) {
|
||||
int detailLevel = grid.getMaxLevelForPrecision(shape,distErrPct);
|
||||
List<Node> cells = grid.getNodes(shape, detailLevel, true);//true=intermediates cells
|
||||
//If shape isn't a point, add a full-resolution center-point so that
|
||||
// PrefixFieldCacheProvider has the center-points.
|
||||
// TODO index each center of a multi-point? Yes/no?
|
||||
if (!(shape instanceof Point)) {
|
||||
Point ctr = shape.getCenter();
|
||||
//TODO should be smarter; don't index 2 tokens for this in CellTokenizer. Harmless though.
|
||||
cells.add(grid.getNodes(ctr,grid.getMaxLevels(),false).get(0));
|
||||
}
|
||||
|
||||
String fname = fieldInfo.getFieldName();
|
||||
if( store ) {
|
||||
//TODO figure out how to re-use original string instead of reconstituting it.
|
||||
String wkt = grid.getSpatialContext().toString(shape);
|
||||
if( index ) {
|
||||
Field f = new Field(fname,wkt,TYPE_STORED);
|
||||
f.setTokenStream(new CellTokenStream(cells.iterator()));
|
||||
return f;
|
||||
}
|
||||
return new StoredField(fname,wkt);
|
||||
}
|
||||
|
||||
if( index ) {
|
||||
return new Field(fname,new CellTokenStream(cells.iterator()),TYPE_UNSTORED);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Fields need to be indexed or store ["+fname+"]");
|
||||
}
|
||||
|
||||
/* Indexed, tokenized, not stored. */
|
||||
public static final FieldType TYPE_UNSTORED = new FieldType();
|
||||
|
||||
/* Indexed, tokenized, stored. */
|
||||
public static final FieldType TYPE_STORED = new FieldType();
|
||||
|
||||
static {
|
||||
TYPE_UNSTORED.setIndexed(true);
|
||||
TYPE_UNSTORED.setTokenized(true);
|
||||
TYPE_UNSTORED.setOmitNorms(true);
|
||||
TYPE_UNSTORED.freeze();
|
||||
|
||||
TYPE_STORED.setStored(true);
|
||||
TYPE_STORED.setIndexed(true);
|
||||
TYPE_STORED.setTokenized(true);
|
||||
TYPE_STORED.setOmitNorms(true);
|
||||
TYPE_STORED.freeze();
|
||||
}
|
||||
|
||||
/** Outputs the tokenString of a cell, and if its a leaf, outputs it again with the leaf byte. */
|
||||
final static class CellTokenStream extends TokenStream {
|
||||
|
||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
||||
|
||||
private Iterator<Node> iter = null;
|
||||
|
||||
public CellTokenStream(Iterator<Node> tokens) {
|
||||
this.iter = tokens;
|
||||
}
|
||||
|
||||
CharSequence nextTokenStringNeedingLeaf = null;
|
||||
|
||||
@Override
|
||||
public boolean incrementToken() throws IOException {
|
||||
clearAttributes();
|
||||
if (nextTokenStringNeedingLeaf != null) {
|
||||
termAtt.append(nextTokenStringNeedingLeaf);
|
||||
termAtt.append((char) Node.LEAF_BYTE);
|
||||
nextTokenStringNeedingLeaf = null;
|
||||
return true;
|
||||
}
|
||||
if (iter.hasNext()) {
|
||||
Node cell = iter.next();
|
||||
CharSequence token = cell.getTokenString();
|
||||
termAtt.append(token);
|
||||
if (cell.isLeaf())
|
||||
nextTokenStringNeedingLeaf = token;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeValueSource(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo) {
|
||||
DistanceCalculator calc = grid.getSpatialContext().getDistCalc();
|
||||
return makeValueSource(args, fieldInfo, calc);
|
||||
}
|
||||
|
||||
public ValueSource makeValueSource(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo, DistanceCalculator calc) {
|
||||
PointPrefixTreeFieldCacheProvider p = provider.get( fieldInfo.getFieldName() );
|
||||
if( p == null ) {
|
||||
synchronized (this) {//double checked locking idiom is okay since provider is threadsafe
|
||||
p = provider.get( fieldInfo.getFieldName() );
|
||||
if (p == null) {
|
||||
p = new PointPrefixTreeFieldCacheProvider(grid, fieldInfo.getFieldName(), defaultFieldValuesArrayLen);
|
||||
provider.put(fieldInfo.getFieldName(),p);
|
||||
}
|
||||
}
|
||||
}
|
||||
Point point = args.getShape().getCenter();
|
||||
return new CachedDistanceValueSource(point, calc, p);
|
||||
}
|
||||
|
||||
public SpatialPrefixTree getGrid() {
|
||||
return grid;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.SpatialRelation;
|
||||
import org.apache.lucene.index.*;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.spatial.prefix.tree.Node;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.OpenBitSet;
|
||||
import org.apache.lucene.util.StringHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Performs a spatial intersection filter against a field indexed with {@link SpatialPrefixTree}, a Trie.
|
||||
* SPT yields terms (grids) at length 1 and at greater lengths corresponding to greater precisions.
|
||||
* This filter recursively traverses each grid length and uses methods on {@link Shape} to efficiently know
|
||||
* that all points at a prefix fit in the shape or not to either short-circuit unnecessary traversals or to efficiently
|
||||
* load all enclosed points.
|
||||
*/
|
||||
public class RecursivePrefixTreeFilter extends Filter {
|
||||
|
||||
/* TODOs for future:
|
||||
|
||||
Can a polygon query shape be optimized / made-simpler at recursive depths (e.g. intersection of shape + cell box)
|
||||
|
||||
RE "scan" threshold:
|
||||
// IF configured to do so, we could use term.freq() as an estimate on the number of places at this depth. OR, perhaps
|
||||
// make estimates based on the total known term count at this level?
|
||||
if (!scan) {
|
||||
//Make some estimations on how many points there are at this level and how few there would need to be to set
|
||||
// !scan to false.
|
||||
long termsThreshold = (long) estimateNumberIndexedTerms(cell.length(),queryShape.getDocFreqExpenseThreshold(cell));
|
||||
long thisOrd = termsEnum.ord();
|
||||
scan = (termsEnum.seek(thisOrd+termsThreshold+1) == TermsEnum.SeekStatus.END
|
||||
|| !cell.contains(termsEnum.term()));
|
||||
termsEnum.seek(thisOrd);//return to last position
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
private final String fieldName;
|
||||
private final SpatialPrefixTree grid;
|
||||
private final Shape queryShape;
|
||||
private final int prefixGridScanLevel;//at least one less than grid.getMaxLevels()
|
||||
private final int detailLevel;
|
||||
|
||||
public RecursivePrefixTreeFilter(String fieldName, SpatialPrefixTree grid, Shape queryShape, int prefixGridScanLevel,
|
||||
int detailLevel) {
|
||||
this.fieldName = fieldName;
|
||||
this.grid = grid;
|
||||
this.queryShape = queryShape;
|
||||
this.prefixGridScanLevel = Math.max(1,Math.min(prefixGridScanLevel,grid.getMaxLevels()-1));
|
||||
this.detailLevel = detailLevel;
|
||||
assert detailLevel <= grid.getMaxLevels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocIdSet getDocIdSet(AtomicReaderContext ctx, Bits acceptDocs) throws IOException {
|
||||
AtomicReader reader = ctx.reader();
|
||||
OpenBitSet bits = new OpenBitSet(reader.maxDoc());
|
||||
Terms terms = reader.terms(fieldName);
|
||||
if (terms == null)
|
||||
return null;
|
||||
TermsEnum termsEnum = terms.iterator(null);
|
||||
DocsEnum docsEnum = null;//cached for termsEnum.docs() calls
|
||||
Node scanCell = null;
|
||||
|
||||
//cells is treated like a stack. LinkedList conveniently has bulk add to beginning. It's in sorted order so that we
|
||||
// always advance forward through the termsEnum index.
|
||||
LinkedList<Node> cells = new LinkedList<Node>(
|
||||
grid.getWorldNode().getSubCells(queryShape) );
|
||||
|
||||
//This is a recursive algorithm that starts with one or more "big" cells, and then recursively dives down into the
|
||||
// first such cell that intersects with the query shape. It's a depth first traversal because we don't move onto
|
||||
// the next big cell (breadth) until we're completely done considering all smaller cells beneath it. For a given
|
||||
// cell, if it's *within* the query shape then we can conveniently short-circuit the depth traversal and
|
||||
// grab all documents assigned to this cell/term. For an intersection of the cell and query shape, we either
|
||||
// recursively step down another grid level or we decide heuristically (via prefixGridScanLevel) that there aren't
|
||||
// that many points, and so we scan through all terms within this cell (i.e. the term starts with the cell's term),
|
||||
// seeing which ones are within the query shape.
|
||||
while(!cells.isEmpty()) {
|
||||
final Node cell = cells.removeFirst();
|
||||
final BytesRef cellTerm = new BytesRef(cell.getTokenBytes());
|
||||
TermsEnum.SeekStatus seekStat = termsEnum.seekCeil(cellTerm);
|
||||
if (seekStat == TermsEnum.SeekStatus.END)
|
||||
break;
|
||||
if (seekStat == TermsEnum.SeekStatus.NOT_FOUND)
|
||||
continue;
|
||||
if (cell.getLevel() == detailLevel || cell.isLeaf()) {
|
||||
docsEnum = termsEnum.docs(acceptDocs, docsEnum, false);
|
||||
addDocs(docsEnum,bits);
|
||||
} else {//any other intersection
|
||||
//If the next indexed term is the leaf marker, then add all of them
|
||||
BytesRef nextCellTerm = termsEnum.next();
|
||||
assert StringHelper.startsWith(nextCellTerm, cellTerm);
|
||||
scanCell = grid.getNode(nextCellTerm.bytes, nextCellTerm.offset, nextCellTerm.length, scanCell);
|
||||
if (scanCell.isLeaf()) {
|
||||
docsEnum = termsEnum.docs(acceptDocs, docsEnum, false);
|
||||
addDocs(docsEnum,bits);
|
||||
termsEnum.next();//move pointer to avoid potential redundant addDocs() below
|
||||
}
|
||||
|
||||
//Decide whether to continue to divide & conquer, or whether it's time to scan through terms beneath this cell.
|
||||
// Scanning is a performance optimization trade-off.
|
||||
boolean scan = cell.getLevel() >= prefixGridScanLevel;//simple heuristic
|
||||
|
||||
if (!scan) {
|
||||
//Divide & conquer
|
||||
cells.addAll(0, cell.getSubCells(queryShape));//add to beginning
|
||||
} else {
|
||||
//Scan through all terms within this cell to see if they are within the queryShape. No seek()s.
|
||||
for(BytesRef term = termsEnum.term(); term != null && StringHelper.startsWith(term,cellTerm); term = termsEnum.next()) {
|
||||
scanCell = grid.getNode(term.bytes, term.offset, term.length, scanCell);
|
||||
int termLevel = scanCell.getLevel();
|
||||
if (termLevel > detailLevel)
|
||||
continue;
|
||||
if (termLevel == detailLevel || scanCell.isLeaf()) {
|
||||
//TODO should put more thought into implications of box vs point
|
||||
Shape cShape = termLevel == grid.getMaxLevels() ? scanCell.getCenter() : scanCell.getShape();
|
||||
if(queryShape.relate(cShape, grid.getSpatialContext()) == SpatialRelation.DISJOINT)
|
||||
continue;
|
||||
|
||||
docsEnum = termsEnum.docs(acceptDocs, docsEnum, false);
|
||||
addDocs(docsEnum,bits);
|
||||
}
|
||||
}//term loop
|
||||
}
|
||||
}
|
||||
}//cell loop
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
private void addDocs(DocsEnum docsEnum, OpenBitSet bits) throws IOException {
|
||||
int docid;
|
||||
while ((docid = docsEnum.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
|
||||
bits.fastSet(docid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoFilter{fieldName='" + fieldName + '\'' + ", shape=" + queryShape + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
RecursivePrefixTreeFilter that = (RecursivePrefixTreeFilter) o;
|
||||
|
||||
if (!fieldName.equals(that.fieldName)) return false;
|
||||
//note that we don't need to look at grid since for the same field it should be the same
|
||||
if (prefixGridScanLevel != that.prefixGridScanLevel) return false;
|
||||
if (detailLevel != that.detailLevel) return false;
|
||||
if (!queryShape.equals(that.queryShape)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = fieldName.hashCode();
|
||||
result = 31 * result + queryShape.hashCode();
|
||||
result = 31 * result + detailLevel;
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.exception.UnsupportedSpatialOperation;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.query.SpatialOperation;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.queries.function.FunctionQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FilteredQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.SimpleSpatialFieldInfo;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
|
||||
|
||||
public class RecursivePrefixTreeStrategy extends PrefixTreeStrategy {
|
||||
|
||||
private int prefixGridScanLevel;//TODO how is this customized?
|
||||
|
||||
public RecursivePrefixTreeStrategy(SpatialPrefixTree grid) {
|
||||
super(grid);
|
||||
prefixGridScanLevel = grid.getMaxLevels() - 4;//TODO this default constant is dependent on the prefix grid size
|
||||
}
|
||||
|
||||
public void setPrefixGridScanLevel(int prefixGridScanLevel) {
|
||||
this.prefixGridScanLevel = prefixGridScanLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"(prefixGridScanLevel:"+prefixGridScanLevel+",SPG:("+ grid +"))";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo) {
|
||||
Filter f = makeFilter(args, fieldInfo);
|
||||
|
||||
ValueSource vs = makeValueSource(args, fieldInfo);
|
||||
return new FilteredQuery( new FunctionQuery(vs), f );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter makeFilter(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo) {
|
||||
final SpatialOperation op = args.getOperation();
|
||||
if (! SpatialOperation.is(op, SpatialOperation.IsWithin, SpatialOperation.Intersects, SpatialOperation.BBoxWithin))
|
||||
throw new UnsupportedSpatialOperation(op);
|
||||
|
||||
Shape qshape = args.getShape();
|
||||
|
||||
int detailLevel = grid.getMaxLevelForPrecision(qshape,args.getDistPrecision());
|
||||
|
||||
return new RecursivePrefixTreeFilter(
|
||||
fieldInfo.getFieldName(), grid,qshape, prefixGridScanLevel, detailLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.exception.UnsupportedSpatialOperation;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.query.SpatialOperation;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.*;
|
||||
import org.apache.lucene.spatial.SimpleSpatialFieldInfo;
|
||||
import org.apache.lucene.spatial.prefix.tree.Node;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TermQueryPrefixTreeStrategy extends PrefixTreeStrategy {
|
||||
|
||||
public TermQueryPrefixTreeStrategy(SpatialPrefixTree grid) {
|
||||
super(grid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter makeFilter(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo) {
|
||||
return new QueryWrapperFilter( makeQuery(args, fieldInfo) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args, SimpleSpatialFieldInfo fieldInfo) {
|
||||
if (args.getOperation() != SpatialOperation.Intersects &&
|
||||
args.getOperation() != SpatialOperation.IsWithin &&
|
||||
args.getOperation() != SpatialOperation.Overlaps ){
|
||||
// TODO -- can translate these other query types
|
||||
throw new UnsupportedSpatialOperation(args.getOperation());
|
||||
}
|
||||
Shape qshape = args.getShape();
|
||||
int detailLevel = grid.getMaxLevelForPrecision(qshape, args.getDistPrecision());
|
||||
List<Node> cells = grid.getNodes(qshape, detailLevel, false);
|
||||
|
||||
BooleanQuery booleanQuery = new BooleanQuery();
|
||||
for (Node cell : cells) {
|
||||
booleanQuery.add(new TermQuery(new Term(fieldInfo.getFieldName(), cell.getTokenString())), BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
return booleanQuery;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +1,22 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier.projections;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public interface IProjector {
|
||||
public String coordsAsString(double latitude, double longitude);
|
||||
public double[] coords(double latitude, double longitude);
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prefix Tree Strategy
|
||||
*/
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.util.GeohashUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* A SpatialPrefixGrid based on Geohashes. Uses {@link GeohashUtils} to do all the geohash work.
|
||||
*/
|
||||
public class GeohashPrefixTree extends SpatialPrefixTree {
|
||||
|
||||
public static class Factory extends SpatialPrefixTreeFactory {
|
||||
|
||||
@Override
|
||||
protected int getLevelForDistance(double degrees) {
|
||||
GeohashPrefixTree grid = new GeohashPrefixTree(ctx, GeohashPrefixTree.getMaxLevelsPossible());
|
||||
return grid.getLevelForDistance(degrees) + 1;//returns 1 greater
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpatialPrefixTree newSPT() {
|
||||
return new GeohashPrefixTree(ctx,
|
||||
maxLevels != null ? maxLevels : GeohashPrefixTree.getMaxLevelsPossible());
|
||||
}
|
||||
}
|
||||
|
||||
public GeohashPrefixTree(SpatialContext ctx, int maxLevels) {
|
||||
super(ctx, maxLevels);
|
||||
Rectangle bounds = ctx.getWorldBounds();
|
||||
if (bounds.getMinX() != -180)
|
||||
throw new IllegalArgumentException("Geohash only supports lat-lon world bounds. Got "+bounds);
|
||||
int MAXP = getMaxLevelsPossible();
|
||||
if (maxLevels <= 0 || maxLevels > MAXP)
|
||||
throw new IllegalArgumentException("maxLen must be [1-"+MAXP+"] but got "+ maxLevels);
|
||||
}
|
||||
|
||||
/** Any more than this and there's no point (double lat & lon are the same). */
|
||||
public static int getMaxLevelsPossible() {
|
||||
return GeohashUtils.MAX_PRECISION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevelForDistance(double dist) {
|
||||
final int level = GeohashUtils.lookupHashLenForWidthHeight(dist, dist);
|
||||
return Math.max(Math.min(level, maxLevels), 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(Point p, int level) {
|
||||
return new GhCell(GeohashUtils.encodeLatLon(p.getY(), p.getX(), level));//args are lat,lon (y,x)
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(String token) {
|
||||
return new GhCell(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(byte[] bytes, int offset, int len) {
|
||||
return new GhCell(bytes, offset, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes(Shape shape, int detailLevel, boolean inclParents) {
|
||||
return shape instanceof Point ? super.getNodesAltPoint((Point) shape, detailLevel, inclParents) :
|
||||
super.getNodes(shape, detailLevel, inclParents);
|
||||
}
|
||||
|
||||
class GhCell extends Node {
|
||||
GhCell(String token) {
|
||||
super(GeohashPrefixTree.this, token);
|
||||
}
|
||||
|
||||
GhCell(byte[] bytes, int off, int len) {
|
||||
super(GeohashPrefixTree.this, bytes, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(byte[] bytes, int off, int len) {
|
||||
super.reset(bytes, off, len);
|
||||
shape = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Node> getSubCells() {
|
||||
String[] hashes = GeohashUtils.getSubGeohashes(getGeohash());//sorted
|
||||
List<Node> cells = new ArrayList<Node>(hashes.length);
|
||||
for (String hash : hashes) {
|
||||
cells.add(new GhCell(hash));
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubCellsSize() {
|
||||
return 32;//8x4
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getSubCell(Point p) {
|
||||
return GeohashPrefixTree.this.getNode(p,getLevel()+1);//not performant!
|
||||
}
|
||||
|
||||
private Shape shape;//cache
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
if (shape == null) {
|
||||
shape = GeohashUtils.decodeBoundary(getGeohash(), ctx);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getCenter() {
|
||||
return GeohashUtils.decode(getGeohash(), ctx);
|
||||
}
|
||||
|
||||
private String getGeohash() {
|
||||
return getTokenString();
|
||||
}
|
||||
|
||||
}//class GhCell
|
||||
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.SpatialRelation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a grid cell. These are not necessarily threadsafe, although new Cell("") (world cell) must be.
|
||||
*/
|
||||
public abstract class Node implements Comparable<Node> {
|
||||
public static final byte LEAF_BYTE = '+';//NOTE: must sort before letters & numbers
|
||||
|
||||
/*
|
||||
Holds a byte[] and/or String representation of the cell. Both are lazy constructed from the other.
|
||||
Neither contains the trailing leaf byte.
|
||||
*/
|
||||
private byte[] bytes;
|
||||
private int b_off;
|
||||
private int b_len;
|
||||
|
||||
private String token;//this is the only part of equality
|
||||
|
||||
protected SpatialRelation shapeRel;//set in getSubCells(filter), and via setLeaf().
|
||||
private SpatialPrefixTree spatialPrefixTree;
|
||||
|
||||
protected Node(SpatialPrefixTree spatialPrefixTree, String token) {
|
||||
this.spatialPrefixTree = spatialPrefixTree;
|
||||
this.token = token;
|
||||
if (token.length() > 0 && token.charAt(token.length() - 1) == (char) LEAF_BYTE) {
|
||||
this.token = token.substring(0, token.length() - 1);
|
||||
setLeaf();
|
||||
}
|
||||
|
||||
if (getLevel() == 0)
|
||||
getShape();//ensure any lazy instantiation completes to make this threadsafe
|
||||
}
|
||||
|
||||
protected Node(SpatialPrefixTree spatialPrefixTree, byte[] bytes, int off, int len) {
|
||||
this.spatialPrefixTree = spatialPrefixTree;
|
||||
this.bytes = bytes;
|
||||
this.b_off = off;
|
||||
this.b_len = len;
|
||||
b_fixLeaf();
|
||||
}
|
||||
|
||||
public void reset(byte[] bytes, int off, int len) {
|
||||
assert getLevel() != 0;
|
||||
token = null;
|
||||
shapeRel = null;
|
||||
this.bytes = bytes;
|
||||
this.b_off = off;
|
||||
this.b_len = len;
|
||||
b_fixLeaf();
|
||||
}
|
||||
|
||||
private void b_fixLeaf() {
|
||||
if (bytes[b_off + b_len - 1] == LEAF_BYTE) {
|
||||
b_len--;
|
||||
setLeaf();
|
||||
} else if (getLevel() == spatialPrefixTree.getMaxLevels()) {
|
||||
setLeaf();
|
||||
}
|
||||
}
|
||||
|
||||
public SpatialRelation getShapeRel() {
|
||||
return shapeRel;
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return shapeRel == SpatialRelation.WITHIN;
|
||||
}
|
||||
|
||||
public void setLeaf() {
|
||||
assert getLevel() != 0;
|
||||
shapeRel = SpatialRelation.WITHIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: doesn't contain a trailing leaf byte.
|
||||
*/
|
||||
public String getTokenString() {
|
||||
if (token == null) {
|
||||
token = new String(bytes, b_off, b_len, SpatialPrefixTree.UTF8);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: doesn't contain a trailing leaf byte.
|
||||
*/
|
||||
public byte[] getTokenBytes() {
|
||||
if (bytes != null) {
|
||||
if (b_off != 0 || b_len != bytes.length) {
|
||||
throw new IllegalStateException("Not supported if byte[] needs to be recreated.");
|
||||
}
|
||||
} else {
|
||||
bytes = token.getBytes(SpatialPrefixTree.UTF8);
|
||||
b_off = 0;
|
||||
b_len = bytes.length;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return token != null ? token.length() : b_len;
|
||||
}
|
||||
|
||||
//TODO add getParent() and update some algorithms to use this?
|
||||
//public Cell getParent();
|
||||
|
||||
/**
|
||||
* Like {@link #getSubCells()} but with the results filtered by a shape. If that shape is a {@link com.spatial4j.core.shape.Point} then it
|
||||
* must call {@link #getSubCell(com.spatial4j.core.shape.Point)};
|
||||
* Precondition: Never called when getLevel() == maxLevel.
|
||||
*
|
||||
* @param shapeFilter an optional filter for the returned cells.
|
||||
* @return A set of cells (no dups), sorted. Not Modifiable.
|
||||
*/
|
||||
public Collection<Node> getSubCells(Shape shapeFilter) {
|
||||
//Note: Higher-performing subclasses might override to consider the shape filter to generate fewer cells.
|
||||
if (shapeFilter instanceof Point) {
|
||||
return Collections.singleton(getSubCell((Point) shapeFilter));
|
||||
}
|
||||
Collection<Node> cells = getSubCells();
|
||||
|
||||
if (shapeFilter == null) {
|
||||
return cells;
|
||||
}
|
||||
List<Node> copy = new ArrayList<Node>(cells.size());//copy since cells contractually isn't modifiable
|
||||
for (Node cell : cells) {
|
||||
SpatialRelation rel = cell.getShape().relate(shapeFilter, spatialPrefixTree.ctx);
|
||||
if (rel == SpatialRelation.DISJOINT)
|
||||
continue;
|
||||
cell.shapeRel = rel;
|
||||
copy.add(cell);
|
||||
}
|
||||
cells = copy;
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performant implementations are expected to implement this efficiently by considering the current
|
||||
* cell's boundary.
|
||||
* Precondition: Never called when getLevel() == maxLevel.
|
||||
* Precondition: this.getShape().relate(p) != DISJOINT.
|
||||
*/
|
||||
public abstract Node getSubCell(Point p);
|
||||
|
||||
//TODO Cell getSubCell(byte b)
|
||||
|
||||
/**
|
||||
* Gets the cells at the next grid cell level that cover this cell.
|
||||
* Precondition: Never called when getLevel() == maxLevel.
|
||||
*
|
||||
* @return A set of cells (no dups), sorted. Not Modifiable.
|
||||
*/
|
||||
protected abstract Collection<Node> getSubCells();
|
||||
|
||||
/**
|
||||
* {@link #getSubCells()}.size() -- usually a constant. Should be >=2
|
||||
*/
|
||||
public abstract int getSubCellsSize();
|
||||
|
||||
public abstract Shape getShape();
|
||||
|
||||
public Point getCenter() {
|
||||
return getShape().getCenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Node o) {
|
||||
return getTokenString().compareTo(o.getTokenString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return !(obj == null || !(obj instanceof Node)) && getTokenString().equals(((Node) obj).getTokenString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getTokenString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getTokenString() + (isLeaf() ? (char) LEAF_BYTE : "");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.SpatialRelation;
|
||||
import com.spatial4j.core.shape.simple.PointImpl;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class QuadPrefixTree extends SpatialPrefixTree {
|
||||
|
||||
public static class Factory extends SpatialPrefixTreeFactory {
|
||||
|
||||
@Override
|
||||
protected int getLevelForDistance(double degrees) {
|
||||
QuadPrefixTree grid = new QuadPrefixTree(ctx, MAX_LEVELS_POSSIBLE);
|
||||
return grid.getLevelForDistance(degrees) + 1;//returns 1 greater
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpatialPrefixTree newSPT() {
|
||||
return new QuadPrefixTree(ctx,
|
||||
maxLevels != null ? maxLevels : MAX_LEVELS_POSSIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int MAX_LEVELS_POSSIBLE = 50;//not really sure how big this should be
|
||||
|
||||
public static final int DEFAULT_MAX_LEVELS = 12;
|
||||
private final double xmin;
|
||||
private final double xmax;
|
||||
private final double ymin;
|
||||
private final double ymax;
|
||||
private final double xmid;
|
||||
private final double ymid;
|
||||
|
||||
private final double gridW;
|
||||
public final double gridH;
|
||||
|
||||
final double[] levelW;
|
||||
final double[] levelH;
|
||||
final int[] levelS; // side
|
||||
final int[] levelN; // number
|
||||
|
||||
public QuadPrefixTree(
|
||||
SpatialContext ctx, Rectangle bounds, int maxLevels) {
|
||||
super(ctx, maxLevels);
|
||||
this.xmin = bounds.getMinX();
|
||||
this.xmax = bounds.getMaxX();
|
||||
this.ymin = bounds.getMinY();
|
||||
this.ymax = bounds.getMaxY();
|
||||
|
||||
levelW = new double[maxLevels];
|
||||
levelH = new double[maxLevels];
|
||||
levelS = new int[maxLevels];
|
||||
levelN = new int[maxLevels];
|
||||
|
||||
gridW = xmax - xmin;
|
||||
gridH = ymax - ymin;
|
||||
this.xmid = xmin + gridW/2.0;
|
||||
this.ymid = ymin + gridH/2.0;
|
||||
levelW[0] = gridW/2.0;
|
||||
levelH[0] = gridH/2.0;
|
||||
levelS[0] = 2;
|
||||
levelN[0] = 4;
|
||||
|
||||
for (int i = 1; i < levelW.length; i++) {
|
||||
levelW[i] = levelW[i - 1] / 2.0;
|
||||
levelH[i] = levelH[i - 1] / 2.0;
|
||||
levelS[i] = levelS[i - 1] * 2;
|
||||
levelN[i] = levelN[i - 1] * 4;
|
||||
}
|
||||
}
|
||||
|
||||
public QuadPrefixTree(SpatialContext ctx) {
|
||||
this(ctx, DEFAULT_MAX_LEVELS);
|
||||
}
|
||||
|
||||
public QuadPrefixTree(
|
||||
SpatialContext ctx, int maxLevels) {
|
||||
this(ctx, ctx.getWorldBounds(), maxLevels);
|
||||
}
|
||||
|
||||
public void printInfo() {
|
||||
NumberFormat nf = NumberFormat.getNumberInstance();
|
||||
nf.setMaximumFractionDigits(5);
|
||||
nf.setMinimumFractionDigits(5);
|
||||
nf.setMinimumIntegerDigits(3);
|
||||
|
||||
for (int i = 0; i < maxLevels; i++) {
|
||||
System.out.println(i + "]\t" + nf.format(levelW[i]) + "\t" + nf.format(levelH[i]) + "\t" +
|
||||
levelS[i] + "\t" + (levelS[i] * levelS[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevelForDistance(double dist) {
|
||||
for (int i = 1; i < maxLevels; i++) {
|
||||
//note: level[i] is actually a lookup for level i+1
|
||||
if(dist > levelW[i] || dist > levelH[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return maxLevels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(Point p, int level) {
|
||||
List<Node> cells = new ArrayList<Node>(1);
|
||||
build(xmid, ymid, 0, cells, new StringBuilder(), new PointImpl(p.getX(),p.getY()), level);
|
||||
return cells.get(0);//note cells could be longer if p on edge
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(String token) {
|
||||
return new QuadCell(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(byte[] bytes, int offset, int len) {
|
||||
return new QuadCell(bytes, offset, len);
|
||||
}
|
||||
|
||||
@Override //for performance
|
||||
public List<Node> getNodes(Shape shape, int detailLevel, boolean inclParents) {
|
||||
if (shape instanceof Point)
|
||||
return super.getNodesAltPoint((Point) shape, detailLevel, inclParents);
|
||||
else
|
||||
return super.getNodes(shape, detailLevel, inclParents);
|
||||
}
|
||||
|
||||
private void build(
|
||||
double x,
|
||||
double y,
|
||||
int level,
|
||||
List<Node> matches,
|
||||
StringBuilder str,
|
||||
Shape shape,
|
||||
int maxLevel) {
|
||||
assert str.length() == level;
|
||||
double w = levelW[level] / 2;
|
||||
double h = levelH[level] / 2;
|
||||
|
||||
// Z-Order
|
||||
// http://en.wikipedia.org/wiki/Z-order_%28curve%29
|
||||
checkBattenberg('A', x - w, y + h, level, matches, str, shape, maxLevel);
|
||||
checkBattenberg('B', x + w, y + h, level, matches, str, shape, maxLevel);
|
||||
checkBattenberg('C', x - w, y - h, level, matches, str, shape, maxLevel);
|
||||
checkBattenberg('D', x + w, y - h, level, matches, str, shape, maxLevel);
|
||||
|
||||
// possibly consider hilbert curve
|
||||
// http://en.wikipedia.org/wiki/Hilbert_curve
|
||||
// http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
|
||||
// if we actually use the range property in the query, this could be useful
|
||||
}
|
||||
|
||||
private void checkBattenberg(
|
||||
char c,
|
||||
double cx,
|
||||
double cy,
|
||||
int level,
|
||||
List<Node> matches,
|
||||
StringBuilder str,
|
||||
Shape shape,
|
||||
int maxLevel) {
|
||||
assert str.length() == level;
|
||||
double w = levelW[level] / 2;
|
||||
double h = levelH[level] / 2;
|
||||
|
||||
int strlen = str.length();
|
||||
Rectangle rectangle = ctx.makeRect(cx - w, cx + w, cy - h, cy + h);
|
||||
SpatialRelation v = shape.relate(rectangle, ctx);
|
||||
if (SpatialRelation.CONTAINS == v) {
|
||||
str.append(c);
|
||||
//str.append(SpatialPrefixGrid.COVER);
|
||||
matches.add(new QuadCell(str.toString(),v.transpose()));
|
||||
} else if (SpatialRelation.DISJOINT == v) {
|
||||
// nothing
|
||||
} else { // SpatialRelation.WITHIN, SpatialRelation.INTERSECTS
|
||||
str.append(c);
|
||||
|
||||
int nextLevel = level+1;
|
||||
if (nextLevel >= maxLevel) {
|
||||
//str.append(SpatialPrefixGrid.INTERSECTS);
|
||||
matches.add(new QuadCell(str.toString(),v.transpose()));
|
||||
} else {
|
||||
build(cx, cy, nextLevel, matches, str, shape, maxLevel);
|
||||
}
|
||||
}
|
||||
str.setLength(strlen);
|
||||
}
|
||||
|
||||
class QuadCell extends Node {
|
||||
|
||||
public QuadCell(String token) {
|
||||
super(QuadPrefixTree.this, token);
|
||||
}
|
||||
|
||||
public QuadCell(String token, SpatialRelation shapeRel) {
|
||||
super(QuadPrefixTree.this, token);
|
||||
this.shapeRel = shapeRel;
|
||||
}
|
||||
|
||||
QuadCell(byte[] bytes, int off, int len) {
|
||||
super(QuadPrefixTree.this, bytes, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(byte[] bytes, int off, int len) {
|
||||
super.reset(bytes, off, len);
|
||||
shape = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Node> getSubCells() {
|
||||
List<Node> cells = new ArrayList<Node>(4);
|
||||
cells.add(new QuadCell(getTokenString()+"A"));
|
||||
cells.add(new QuadCell(getTokenString()+"B"));
|
||||
cells.add(new QuadCell(getTokenString()+"C"));
|
||||
cells.add(new QuadCell(getTokenString()+"D"));
|
||||
return cells;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubCellsSize() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getSubCell(Point p) {
|
||||
return QuadPrefixTree.this.getNode(p,getLevel()+1);//not performant!
|
||||
}
|
||||
|
||||
private Shape shape;//cache
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
if (shape == null)
|
||||
shape = makeShape();
|
||||
return shape;
|
||||
}
|
||||
|
||||
private Rectangle makeShape() {
|
||||
String token = getTokenString();
|
||||
double xmin = QuadPrefixTree.this.xmin;
|
||||
double ymin = QuadPrefixTree.this.ymin;
|
||||
|
||||
for (int i = 0; i < token.length(); i++) {
|
||||
char c = token.charAt(i);
|
||||
if ('A' == c || 'a' == c) {
|
||||
ymin += levelH[i];
|
||||
} else if ('B' == c || 'b' == c) {
|
||||
xmin += levelW[i];
|
||||
ymin += levelH[i];
|
||||
} else if ('C' == c || 'c' == c) {
|
||||
// nothing really
|
||||
}
|
||||
else if('D' == c || 'd' == c) {
|
||||
xmin += levelW[i];
|
||||
} else {
|
||||
throw new RuntimeException("unexpected char: " + c);
|
||||
}
|
||||
}
|
||||
int len = token.length();
|
||||
double width, height;
|
||||
if (len > 0) {
|
||||
width = levelW[len-1];
|
||||
height = levelH[len-1];
|
||||
} else {
|
||||
width = gridW;
|
||||
height = gridH;
|
||||
}
|
||||
return ctx.makeRect(xmin, xmin + width, ymin, ymin + height);
|
||||
}
|
||||
}//QuadCell
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Spatial Prefix Tree, or Trie, which decomposes shapes into prefixed strings at variable lengths corresponding to
|
||||
* variable precision. Each string corresponds to a spatial region.
|
||||
*
|
||||
* Implementations of this class should be thread-safe and immutable once initialized.
|
||||
*/
|
||||
public abstract class SpatialPrefixTree {
|
||||
|
||||
protected static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
protected final int maxLevels;
|
||||
|
||||
protected final SpatialContext ctx;
|
||||
|
||||
public SpatialPrefixTree(SpatialContext ctx, int maxLevels) {
|
||||
assert maxLevels > 0;
|
||||
this.ctx = ctx;
|
||||
this.maxLevels = maxLevels;
|
||||
}
|
||||
|
||||
public SpatialContext getSpatialContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public int getMaxLevels() {
|
||||
return maxLevels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(maxLevels:" + maxLevels + ",ctx:" + ctx + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link com.spatial4j.core.query.SpatialArgs#getDistPrecision()}.
|
||||
* A grid level looked up via {@link #getLevelForDistance(double)} is returned.
|
||||
*
|
||||
* @param shape
|
||||
* @param precision 0-0.5
|
||||
* @return 1-maxLevels
|
||||
*/
|
||||
public int getMaxLevelForPrecision(Shape shape, double precision) {
|
||||
if (precision < 0 || precision > 0.5) {
|
||||
throw new IllegalArgumentException("Precision " + precision + " must be between [0-0.5]");
|
||||
}
|
||||
if (precision == 0 || shape instanceof Point) {
|
||||
return maxLevels;
|
||||
}
|
||||
double bboxArea = shape.getBoundingBox().getArea();
|
||||
if (bboxArea == 0) {
|
||||
return maxLevels;
|
||||
}
|
||||
double avgSideLenFromCenter = Math.sqrt(bboxArea) / 2;
|
||||
return getLevelForDistance(avgSideLenFromCenter * precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the level of the smallest grid size with a side length that is greater or equal to the provided
|
||||
* distance.
|
||||
*
|
||||
* @param dist >= 0
|
||||
* @return level [1-maxLevels]
|
||||
*/
|
||||
public abstract int getLevelForDistance(double dist);
|
||||
|
||||
//TODO double getDistanceForLevel(int level)
|
||||
|
||||
private transient Node worldNode;//cached
|
||||
|
||||
/**
|
||||
* Returns the level 0 cell which encompasses all spatial data. Equivalent to {@link #getNode(String)} with "".
|
||||
* This cell is threadsafe, just like a spatial prefix grid is, although cells aren't
|
||||
* generally threadsafe.
|
||||
* TODO rename to getTopCell or is this fine?
|
||||
*/
|
||||
public Node getWorldNode() {
|
||||
if (worldNode == null) {
|
||||
worldNode = getNode("");
|
||||
}
|
||||
return worldNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cell for the specified token. The empty string should be equal to {@link #getWorldNode()}.
|
||||
* Precondition: Never called when token length > maxLevel.
|
||||
*/
|
||||
public abstract Node getNode(String token);
|
||||
|
||||
public abstract Node getNode(byte[] bytes, int offset, int len);
|
||||
|
||||
public final Node getNode(byte[] bytes, int offset, int len, Node target) {
|
||||
if (target == null) {
|
||||
return getNode(bytes, offset, len);
|
||||
}
|
||||
|
||||
target.reset(bytes, offset, len);
|
||||
return target;
|
||||
}
|
||||
|
||||
protected Node getNode(Point p, int level) {
|
||||
return getNodes(p, level, false).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the intersecting & including cells for the specified shape, without exceeding detail level.
|
||||
* The result is a set of cells (no dups), sorted. Unmodifiable.
|
||||
* <p/>
|
||||
* This implementation checks if shape is a Point and if so uses an implementation that
|
||||
* recursively calls {@link Node#getSubCell(com.spatial4j.core.shape.Point)}. Cell subclasses
|
||||
* ideally implement that method with a quick implementation, otherwise, subclasses should
|
||||
* override this method to invoke {@link #getNodesAltPoint(com.spatial4j.core.shape.Point, int, boolean)}.
|
||||
* TODO consider another approach returning an iterator -- won't build up all cells in memory.
|
||||
*/
|
||||
public List<Node> getNodes(Shape shape, int detailLevel, boolean inclParents) {
|
||||
if (detailLevel > maxLevels) {
|
||||
throw new IllegalArgumentException("detailLevel > maxLevels");
|
||||
}
|
||||
|
||||
List<Node> cells;
|
||||
if (shape instanceof Point) {
|
||||
//optimized point algorithm
|
||||
final int initialCapacity = inclParents ? 1 + detailLevel : 1;
|
||||
cells = new ArrayList<Node>(initialCapacity);
|
||||
recursiveGetNodes(getWorldNode(), (Point) shape, detailLevel, true, cells);
|
||||
assert cells.size() == initialCapacity;
|
||||
} else {
|
||||
cells = new ArrayList<Node>(inclParents ? 1024 : 512);
|
||||
recursiveGetNodes(getWorldNode(), shape, detailLevel, inclParents, cells);
|
||||
}
|
||||
if (inclParents) {
|
||||
Node c = cells.remove(0);//remove getWorldNode()
|
||||
assert c.getLevel() == 0;
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
private void recursiveGetNodes(Node node, Shape shape, int detailLevel, boolean inclParents,
|
||||
Collection<Node> result) {
|
||||
if (node.isLeaf()) {//cell is within shape
|
||||
result.add(node);
|
||||
return;
|
||||
}
|
||||
final Collection<Node> subCells = node.getSubCells(shape);
|
||||
if (node.getLevel() == detailLevel - 1) {
|
||||
if (subCells.size() < node.getSubCellsSize()) {
|
||||
if (inclParents)
|
||||
result.add(node);
|
||||
for (Node subCell : subCells) {
|
||||
subCell.setLeaf();
|
||||
}
|
||||
result.addAll(subCells);
|
||||
} else {//a bottom level (i.e. detail level) optimization where all boxes intersect, so use parent cell.
|
||||
node.setLeaf();
|
||||
result.add(node);
|
||||
}
|
||||
} else {
|
||||
if (inclParents) {
|
||||
result.add(node);
|
||||
}
|
||||
for (Node subCell : subCells) {
|
||||
recursiveGetNodes(subCell, shape, detailLevel, inclParents, result);//tail call
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void recursiveGetNodes(Node node, Point point, int detailLevel, boolean inclParents,
|
||||
Collection<Node> result) {
|
||||
if (inclParents) {
|
||||
result.add(node);
|
||||
}
|
||||
final Node pCell = node.getSubCell(point);
|
||||
if (node.getLevel() == detailLevel - 1) {
|
||||
pCell.setLeaf();
|
||||
result.add(pCell);
|
||||
} else {
|
||||
recursiveGetNodes(pCell, point, detailLevel, inclParents, result);//tail call
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses might override {@link #getNodes(com.spatial4j.core.shape.Shape, int, boolean)}
|
||||
* and check if the argument is a shape and if so, delegate
|
||||
* to this implementation, which calls {@link #getNode(com.spatial4j.core.shape.Point, int)} and
|
||||
* then calls {@link #getNode(String)} repeatedly if inclParents is true.
|
||||
*/
|
||||
protected final List<Node> getNodesAltPoint(Point p, int detailLevel, boolean inclParents) {
|
||||
Node cell = getNode(p, detailLevel);
|
||||
if (!inclParents) {
|
||||
return Collections.singletonList(cell);
|
||||
}
|
||||
|
||||
String endToken = cell.getTokenString();
|
||||
assert endToken.length() == detailLevel;
|
||||
List<Node> cells = new ArrayList<Node>(detailLevel);
|
||||
for (int i = 1; i < detailLevel; i++) {
|
||||
cells.add(getNode(endToken.substring(0, i)));
|
||||
}
|
||||
cells.add(cell);
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add the trailing leaf byte for leaves. This isn't particularly efficient.
|
||||
*/
|
||||
public static List<String> nodesToTokenStrings(Collection<Node> nodes) {
|
||||
List<String> tokens = new ArrayList<String>((nodes.size()));
|
||||
for (Node node : nodes) {
|
||||
final String token = node.getTokenString();
|
||||
if (node.isLeaf()) {
|
||||
tokens.add(token + (char) Node.LEAF_BYTE);
|
||||
} else {
|
||||
tokens.add(token);
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.distance.DistanceUnits;
|
||||
import com.spatial4j.core.distance.DistanceUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dsmiley
|
||||
*/
|
||||
public abstract class SpatialPrefixTreeFactory {
|
||||
|
||||
private static final double DEFAULT_GEO_MAX_DETAIL_KM = 0.001;//1m
|
||||
|
||||
protected Map<String, String> args;
|
||||
protected SpatialContext ctx;
|
||||
protected Integer maxLevels;
|
||||
|
||||
/**
|
||||
* The factory is looked up via "prefixTree" in args, expecting "geohash" or "quad".
|
||||
* If its neither of these, then "geohash" is chosen for a geo context, otherwise "quad" is chosen.
|
||||
*/
|
||||
public static SpatialPrefixTree makeSPT(Map<String,String> args, ClassLoader classLoader, SpatialContext ctx) {
|
||||
SpatialPrefixTreeFactory instance;
|
||||
String cname = args.get("prefixTree");
|
||||
if (cname == null)
|
||||
cname = ctx.isGeo() ? "geohash" : "quad";
|
||||
if ("geohash".equalsIgnoreCase(cname))
|
||||
instance = new GeohashPrefixTree.Factory();
|
||||
else if ("quad".equalsIgnoreCase(cname))
|
||||
instance = new QuadPrefixTree.Factory();
|
||||
else {
|
||||
try {
|
||||
Class c = classLoader.loadClass(cname);
|
||||
instance = (SpatialPrefixTreeFactory) c.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
instance.init(args,ctx);
|
||||
return instance.newSPT();
|
||||
}
|
||||
|
||||
protected void init(Map<String, String> args, SpatialContext ctx) {
|
||||
this.args = args;
|
||||
this.ctx = ctx;
|
||||
initMaxLevels();
|
||||
}
|
||||
|
||||
protected void initMaxLevels() {
|
||||
String mlStr = args.get("maxLevels");
|
||||
if (mlStr != null) {
|
||||
maxLevels = Integer.valueOf(mlStr);
|
||||
return;
|
||||
}
|
||||
|
||||
double degrees;
|
||||
String maxDetailDistStr = args.get("maxDetailDist");
|
||||
if (maxDetailDistStr == null) {
|
||||
if (!ctx.isGeo()) {
|
||||
return;//let default to max
|
||||
}
|
||||
degrees = DistanceUtils.dist2Degrees(DEFAULT_GEO_MAX_DETAIL_KM, DistanceUnits.KILOMETERS.earthRadius());
|
||||
} else {
|
||||
degrees = DistanceUtils.dist2Degrees(Double.parseDouble(maxDetailDistStr), ctx.getUnits().earthRadius());
|
||||
}
|
||||
maxLevels = getLevelForDistance(degrees) + 1;//returns 1 greater
|
||||
}
|
||||
|
||||
/** Calls {@link SpatialPrefixTree#getLevelForDistance(double)}. */
|
||||
protected abstract int getLevelForDistance(double degrees);
|
||||
|
||||
protected abstract SpatialPrefixTree newSPT();
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Spatial Prefix package supports spatial indexing by index-time tokens
|
||||
* where adding characters to a string gives greater resolution.
|
||||
*
|
||||
* Potential Implementations include:
|
||||
* * http://en.wikipedia.org/wiki/Quadtree
|
||||
* * http://en.wikipedia.org/wiki/Geohash
|
||||
* * http://healpix.jpl.nasa.gov/
|
||||
*/
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* An implementation of the Lucene ValueSource model to support spatial relevance ranking.
|
||||
*
|
||||
*/
|
||||
public class CachedDistanceValueSource extends ValueSource {
|
||||
|
||||
private final ShapeFieldCacheProvider<Point> provider;
|
||||
private final DistanceCalculator calculator;
|
||||
private final Point from;
|
||||
|
||||
public CachedDistanceValueSource(Point from, DistanceCalculator calc, ShapeFieldCacheProvider<Point> provider) {
|
||||
this.from = from;
|
||||
this.provider = provider;
|
||||
this.calculator = calc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "DistanceValueSource("+calculator+")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||
final ShapeFieldCache<Point> cache =
|
||||
provider.getCache(readerContext.reader());
|
||||
|
||||
return new FunctionValues() {
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float) doubleVal(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
List<Point> vals = cache.getShapes( doc );
|
||||
if( vals != null ) {
|
||||
double v = calculator.distance(from, vals.get(0));
|
||||
for( int i=1; i<vals.size(); i++ ) {
|
||||
v = Math.min(v, calculator.distance(from, vals.get(i)));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
return Double.NaN; // ?? maybe max?
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) {
|
||||
return description() + "=" + floatVal(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CachedDistanceValueSource that = (CachedDistanceValueSource) o;
|
||||
|
||||
if (calculator != null ? !calculator.equals(that.calculator) : that.calculator != null) return false;
|
||||
if (from != null ? !from.equals(that.from) : that.from != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = calculator != null ? calculator.hashCode() : 0;
|
||||
result = 31 * result + (from != null ? from.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CachingDoubleValueSource extends ValueSource {
|
||||
|
||||
final ValueSource source;
|
||||
final Map<Integer, Double> cache;
|
||||
|
||||
public CachingDoubleValueSource( ValueSource source )
|
||||
{
|
||||
this.source = source;
|
||||
cache = new HashMap<Integer, Double>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Cached["+source.description()+"]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||
final int base = readerContext.docBase;
|
||||
final FunctionValues vals = source.getValues(context,readerContext);
|
||||
return new FunctionValues() {
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
Integer key = Integer.valueOf( base+doc );
|
||||
Double v = cache.get( key );
|
||||
if( v == null ) {
|
||||
v = Double.valueOf( vals.doubleVal(doc) );
|
||||
cache.put( key, v );
|
||||
}
|
||||
return v.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float)doubleVal(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) {
|
||||
return doubleVal(doc)+"";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CachingDoubleValueSource that = (CachingDoubleValueSource) o;
|
||||
|
||||
if (source != null ? !source.equals(that.source) : that.source != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return source != null ? source.hashCode() : 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.document.DoubleField;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
|
||||
/**
|
||||
* Hold some of the parameters used by solr...
|
||||
*/
|
||||
public class NumericFieldInfo {
|
||||
public int precisionStep = 8; // same as solr default
|
||||
public boolean store = true;
|
||||
public boolean index = true;
|
||||
|
||||
public void setPrecisionStep( int p ) {
|
||||
precisionStep = p;
|
||||
if (precisionStep<=0 || precisionStep>=64)
|
||||
precisionStep=Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public IndexableField createDouble( String name, double v ) {
|
||||
if (!store && !index)
|
||||
throw new IllegalArgumentException("field must be indexed or stored");
|
||||
|
||||
FieldType fieldType = new FieldType(DoubleField.TYPE);
|
||||
fieldType.setStored(store);
|
||||
fieldType.setIndexed(index);
|
||||
fieldType.setNumericPrecisionStep(precisionStep);
|
||||
return new DoubleField(name,v,fieldType);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* 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.
|
||||
|
@ -15,38 +15,32 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.tier;
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public class Shape {
|
||||
public class ShapeFieldCache<T extends Shape> {
|
||||
private List<T>[] cache;
|
||||
public int defaultLength;
|
||||
|
||||
private List<Double> area = new ArrayList<Double>();
|
||||
private String tierId;
|
||||
|
||||
public Shape (String tierId){
|
||||
this.tierId = tierId;
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public ShapeFieldCache( int length, int defaultLength ) {
|
||||
cache = new List[length];
|
||||
this.defaultLength= defaultLength;
|
||||
}
|
||||
|
||||
public void addBox(double boxId){
|
||||
area.add(boxId);
|
||||
public void add( int docid, T s ) {
|
||||
List<T> list = cache[docid];
|
||||
if( list == null ) {
|
||||
list = cache[docid] = new ArrayList<T>(defaultLength);
|
||||
}
|
||||
list.add( s );
|
||||
}
|
||||
|
||||
public List<Double> getArea(){
|
||||
return area;
|
||||
}
|
||||
|
||||
public String getTierId(){
|
||||
return tierId;
|
||||
}
|
||||
|
||||
public boolean isInside(double boxId){
|
||||
return area.contains(boxId);
|
||||
|
||||
public List<T> getShapes( int docid ) {
|
||||
return cache[docid];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.index.*;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class ShapeFieldCacheProvider<T extends Shape> {
|
||||
private Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
// it may be a List<T> or T
|
||||
WeakHashMap<IndexReader, ShapeFieldCache<T>> sidx = new WeakHashMap<IndexReader, ShapeFieldCache<T>>();
|
||||
|
||||
protected final int defaultSize;
|
||||
protected final String shapeField;
|
||||
|
||||
public ShapeFieldCacheProvider(String shapeField, int defaultSize) {
|
||||
this.shapeField = shapeField;
|
||||
this.defaultSize = defaultSize;
|
||||
}
|
||||
|
||||
protected abstract T readShape( BytesRef term );
|
||||
|
||||
public synchronized ShapeFieldCache<T> getCache(AtomicReader reader) throws IOException {
|
||||
ShapeFieldCache<T> idx = sidx.get(reader);
|
||||
if (idx != null) {
|
||||
return idx;
|
||||
}
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
log.fine("Building Cache [" + reader.maxDoc() + "]");
|
||||
idx = new ShapeFieldCache<T>(reader.maxDoc(),defaultSize);
|
||||
int count = 0;
|
||||
DocsEnum docs = null;
|
||||
Terms terms = reader.terms(shapeField);
|
||||
TermsEnum te = null;
|
||||
if (terms != null) {
|
||||
te = terms.iterator(te);
|
||||
BytesRef term = te.next();
|
||||
while (term != null) {
|
||||
T shape = readShape(term);
|
||||
if( shape != null ) {
|
||||
docs = te.docs(null, docs, false);
|
||||
Integer docid = docs.nextDoc();
|
||||
while (docid != DocIdSetIterator.NO_MORE_DOCS) {
|
||||
idx.add( docid, shape );
|
||||
docid = docs.nextDoc();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
term = te.next();
|
||||
}
|
||||
}
|
||||
sidx.put(reader, idx);
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
log.fine("Cached: [" + count + " in " + elapsed + "ms] " + idx);
|
||||
return idx;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Put a list of strings directly into the token stream
|
||||
*/
|
||||
public final class StringListTokenizer extends TokenStream {
|
||||
|
||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
||||
|
||||
private final Iterable<String> tokens;
|
||||
private Iterator<String> iter = null;
|
||||
|
||||
public StringListTokenizer(Iterable<String> tokens) {
|
||||
this.tokens = tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementToken() {
|
||||
if (iter.hasNext()) {
|
||||
clearAttributes();
|
||||
String t = iter.next();
|
||||
termAtt.append(t);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
super.reset();
|
||||
iter = tokens.iterator();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* 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.
|
||||
|
@ -15,43 +15,35 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.geometry.shape;
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
|
||||
/**
|
||||
* Common set of operations available on 2d shapes.
|
||||
*
|
||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||
* flux and might change in incompatible ways in the next
|
||||
* release.</font>
|
||||
*/
|
||||
public interface Geometry2D {
|
||||
/**
|
||||
* Translate according to the vector
|
||||
* @param v
|
||||
*/
|
||||
public void translate(Vector2D v);
|
||||
|
||||
/**
|
||||
* Does the shape contain the given point
|
||||
* @param p
|
||||
*/
|
||||
public boolean contains(Point2D p);
|
||||
|
||||
/**
|
||||
* Return the area
|
||||
*/
|
||||
public double area();
|
||||
|
||||
/**
|
||||
* Return the centroid
|
||||
*/
|
||||
public Point2D centroid();
|
||||
|
||||
/**
|
||||
* Returns information about how this shape intersects the given rectangle
|
||||
* @param r
|
||||
*/
|
||||
public IntersectCase intersect(Rectangle r);
|
||||
import org.apache.lucene.analysis.TokenFilter;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TruncateFilter extends TokenFilter {
|
||||
|
||||
private final int maxTokenLength;
|
||||
|
||||
private final CharTermAttribute termAttr = addAttribute(CharTermAttribute.class);
|
||||
|
||||
public TruncateFilter(TokenStream in, int maxTokenLength) {
|
||||
super(in);
|
||||
this.maxTokenLength = maxTokenLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean incrementToken() throws IOException {
|
||||
if (!input.incrementToken()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (termAttr.length() > maxTokenLength) {
|
||||
termAttr.setLength(maxTokenLength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FilteredDocIdSet;
|
||||
import org.apache.lucene.util.Bits;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ValueSourceFilter extends Filter {
|
||||
|
||||
final Filter startingFilter;
|
||||
final ValueSource source;
|
||||
final double min;
|
||||
final double max;
|
||||
|
||||
public ValueSourceFilter( Filter startingFilter, ValueSource source, double min, double max )
|
||||
{
|
||||
if (startingFilter == null) {
|
||||
throw new IllegalArgumentException("please provide a non-null startingFilter; you can use QueryWrapperFilter(MatchAllDocsQuery) as a no-op filter");
|
||||
}
|
||||
this.startingFilter = startingFilter;
|
||||
this.source = source;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
|
||||
final FunctionValues values = source.getValues( null, context );
|
||||
return new FilteredDocIdSet(startingFilter.getDocIdSet(context, acceptDocs)) {
|
||||
@Override
|
||||
public boolean match(int doc) {
|
||||
double val = values.doubleVal( doc );
|
||||
return val > min && val < max;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.simple.PointImpl;
|
||||
import org.apache.lucene.index.AtomicReader;
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.FieldCache;
|
||||
import org.apache.lucene.search.FieldCache.DoubleParser;
|
||||
import org.apache.lucene.util.Bits;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* An implementation of the Lucene ValueSource model to support spatial relevance ranking.
|
||||
*
|
||||
*/
|
||||
public class DistanceValueSource extends ValueSource {
|
||||
|
||||
private final TwoDoublesFieldInfo fields;
|
||||
private final DistanceCalculator calculator;
|
||||
private final Point from;
|
||||
private final DoubleParser parser;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public DistanceValueSource(Point from, DistanceCalculator calc, TwoDoublesFieldInfo fields, DoubleParser parser) {
|
||||
this.from = from;
|
||||
this.fields = fields;
|
||||
this.calculator = calc;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ValueSource description.
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "DistanceValueSource("+calculator+")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the FunctionValues used by the function query.
|
||||
*/
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||
AtomicReader reader = readerContext.reader();
|
||||
|
||||
final double[] ptX = FieldCache.DEFAULT.getDoubles(reader, fields.getFieldNameX(), true);
|
||||
final double[] ptY = FieldCache.DEFAULT.getDoubles(reader, fields.getFieldNameY(), true);
|
||||
final Bits validX = FieldCache.DEFAULT.getDocsWithField(reader, fields.getFieldNameX());
|
||||
final Bits validY = FieldCache.DEFAULT.getDocsWithField(reader, fields.getFieldNameY());
|
||||
|
||||
return new FunctionValues() {
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float) doubleVal(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
// make sure it has minX and area
|
||||
if (validX.get(doc) && validY.get(doc)) {
|
||||
PointImpl pt = new PointImpl( ptX[doc], ptY[doc] );
|
||||
return calculator.distance(from, pt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) {
|
||||
return description() + "=" + floatVal(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this ValueSource is equal to another.
|
||||
* @param o the ValueSource to compare
|
||||
* @return <code>true</code> if the two objects are based upon the same query envelope
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DistanceValueSource that = (DistanceValueSource) o;
|
||||
|
||||
if (calculator != null ? !calculator.equals(that.calculator) : that.calculator != null) return false;
|
||||
if (fields != null ? !fields.equals(that.fields) : that.fields != null) return false;
|
||||
if (from != null ? !from.equals(that.from) : that.from != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = fields != null ? fields.hashCode() : 0;
|
||||
result = 31 * result + (calculator != null ? calculator.hashCode() : 0);
|
||||
result = 31 * result + (from != null ? from.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import org.apache.lucene.spatial.SpatialFieldInfo;
|
||||
|
||||
public class TwoDoublesFieldInfo implements SpatialFieldInfo {
|
||||
|
||||
public static final String SUFFIX_X = "__x";
|
||||
public static final String SUFFIX_Y = "__y";
|
||||
|
||||
private final String fieldName;
|
||||
private final String fieldNameX;
|
||||
private final String fieldNameY;
|
||||
|
||||
public TwoDoublesFieldInfo(String fieldNamePrefix) {
|
||||
fieldName = fieldNamePrefix;
|
||||
fieldNameX = fieldNamePrefix + SUFFIX_X;
|
||||
fieldNameY = fieldNamePrefix + SUFFIX_Y;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public String getFieldNameX() {
|
||||
return fieldNameX;
|
||||
}
|
||||
|
||||
public String getFieldNameY() {
|
||||
return fieldNameY;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.exception.InvalidShapeException;
|
||||
import com.spatial4j.core.exception.UnsupportedSpatialOperation;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.query.SpatialOperation;
|
||||
import com.spatial4j.core.shape.Circle;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.queries.function.FunctionQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.*;
|
||||
import org.apache.lucene.search.FieldCache.DoubleParser;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.util.CachingDoubleValueSource;
|
||||
import org.apache.lucene.spatial.util.NumericFieldInfo;
|
||||
import org.apache.lucene.spatial.util.ValueSourceFilter;
|
||||
|
||||
public class TwoDoublesStrategy extends SpatialStrategy<TwoDoublesFieldInfo> {
|
||||
|
||||
private final NumericFieldInfo finfo;
|
||||
private final DoubleParser parser;
|
||||
|
||||
public TwoDoublesStrategy(SpatialContext ctx, NumericFieldInfo finfo, DoubleParser parser) {
|
||||
super(ctx);
|
||||
this.finfo = finfo;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPolyField() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexableField[] createFields(TwoDoublesFieldInfo fieldInfo,
|
||||
Shape shape, boolean index, boolean store) {
|
||||
if( shape instanceof Point ) {
|
||||
Point point = (Point)shape;
|
||||
|
||||
IndexableField[] f = new IndexableField[(index ? 2 : 0) + (store ? 1 : 0)];
|
||||
if (index) {
|
||||
f[0] = finfo.createDouble( fieldInfo.getFieldNameX(), point.getX() );
|
||||
f[1] = finfo.createDouble( fieldInfo.getFieldNameY(), point.getY() );
|
||||
}
|
||||
if(store) {
|
||||
FieldType customType = new FieldType();
|
||||
customType.setStored(true);
|
||||
f[f.length-1] = new Field( fieldInfo.getFieldName(), ctx.toString( shape ), customType );
|
||||
}
|
||||
return f;
|
||||
}
|
||||
if( !ignoreIncompatibleGeometry ) {
|
||||
throw new IllegalArgumentException( "TwoDoublesStrategy can not index: "+shape );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexableField createField(TwoDoublesFieldInfo indexInfo, Shape shape,
|
||||
boolean index, boolean store) {
|
||||
throw new UnsupportedOperationException("Point is poly field");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeValueSource(SpatialArgs args, TwoDoublesFieldInfo fieldInfo) {
|
||||
Point p = args.getShape().getCenter();
|
||||
return new DistanceValueSource(p, ctx.getDistCalc(), fieldInfo, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter makeFilter(SpatialArgs args, TwoDoublesFieldInfo fieldInfo) {
|
||||
if( args.getShape() instanceof Circle) {
|
||||
if( SpatialOperation.is( args.getOperation(),
|
||||
SpatialOperation.Intersects,
|
||||
SpatialOperation.IsWithin )) {
|
||||
Circle circle = (Circle)args.getShape();
|
||||
Query bbox = makeWithin(circle.getBoundingBox(), fieldInfo);
|
||||
|
||||
// Make the ValueSource
|
||||
ValueSource valueSource = makeValueSource(args, fieldInfo);
|
||||
|
||||
return new ValueSourceFilter(
|
||||
new QueryWrapperFilter( bbox ), valueSource, 0, circle.getDistance() );
|
||||
}
|
||||
}
|
||||
return new QueryWrapperFilter( makeQuery(args, fieldInfo) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args, TwoDoublesFieldInfo fieldInfo) {
|
||||
// For starters, just limit the bbox
|
||||
Shape shape = args.getShape();
|
||||
if (!(shape instanceof Rectangle)) {
|
||||
throw new InvalidShapeException("A rectangle is the only supported shape (so far), not "+shape.getClass());//TODO
|
||||
}
|
||||
Rectangle bbox = (Rectangle) shape;
|
||||
if (bbox.getCrossesDateLine()) {
|
||||
throw new UnsupportedOperationException( "Crossing dateline not yet supported" );
|
||||
}
|
||||
|
||||
ValueSource valueSource = null;
|
||||
|
||||
Query spatial = null;
|
||||
SpatialOperation op = args.getOperation();
|
||||
|
||||
if( SpatialOperation.is( op,
|
||||
SpatialOperation.BBoxWithin,
|
||||
SpatialOperation.BBoxIntersects ) ) {
|
||||
spatial = makeWithin(bbox, fieldInfo);
|
||||
}
|
||||
else if( SpatialOperation.is( op,
|
||||
SpatialOperation.Intersects,
|
||||
SpatialOperation.IsWithin ) ) {
|
||||
spatial = makeWithin(bbox, fieldInfo);
|
||||
if( args.getShape() instanceof Circle) {
|
||||
Circle circle = (Circle)args.getShape();
|
||||
|
||||
// Make the ValueSource
|
||||
valueSource = makeValueSource(args, fieldInfo);
|
||||
|
||||
ValueSourceFilter vsf = new ValueSourceFilter(
|
||||
new QueryWrapperFilter( spatial ), valueSource, 0, circle.getDistance() );
|
||||
|
||||
spatial = new FilteredQuery( new MatchAllDocsQuery(), vsf );
|
||||
}
|
||||
}
|
||||
else if( op == SpatialOperation.IsDisjointTo ) {
|
||||
spatial = makeDisjoint(bbox, fieldInfo);
|
||||
}
|
||||
|
||||
if( spatial == null ) {
|
||||
throw new UnsupportedSpatialOperation(args.getOperation());
|
||||
}
|
||||
|
||||
if( valueSource != null ) {
|
||||
valueSource = new CachingDoubleValueSource(valueSource);
|
||||
}
|
||||
else {
|
||||
valueSource = makeValueSource(args, fieldInfo);
|
||||
}
|
||||
Query spatialRankingQuery = new FunctionQuery(valueSource);
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
bq.add(spatial,BooleanClause.Occur.MUST);
|
||||
bq.add(spatialRankingQuery,BooleanClause.Occur.MUST);
|
||||
return bq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a query to retrieve documents that fully contain the input envelope.
|
||||
* @return the spatial query
|
||||
*/
|
||||
private Query makeWithin(Rectangle bbox, TwoDoublesFieldInfo fieldInfo) {
|
||||
Query qX = NumericRangeQuery.newDoubleRange(
|
||||
fieldInfo.getFieldNameX(),
|
||||
finfo.precisionStep,
|
||||
bbox.getMinX(),
|
||||
bbox.getMaxX(),
|
||||
true,
|
||||
true);
|
||||
Query qY = NumericRangeQuery.newDoubleRange(
|
||||
fieldInfo.getFieldNameY(),
|
||||
finfo.precisionStep,
|
||||
bbox.getMinY(),
|
||||
bbox.getMaxY(),
|
||||
true,
|
||||
true);
|
||||
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
bq.add(qX,BooleanClause.Occur.MUST);
|
||||
bq.add(qY,BooleanClause.Occur.MUST);
|
||||
return bq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a query to retrieve documents that fully contain the input envelope.
|
||||
* @return the spatial query
|
||||
*/
|
||||
Query makeDisjoint(Rectangle bbox, TwoDoublesFieldInfo fieldInfo) {
|
||||
Query qX = NumericRangeQuery.newDoubleRange(
|
||||
fieldInfo.getFieldNameX(),
|
||||
finfo.precisionStep,
|
||||
bbox.getMinX(),
|
||||
bbox.getMaxX(),
|
||||
true,
|
||||
true);
|
||||
Query qY = NumericRangeQuery.newDoubleRange(
|
||||
fieldInfo.getFieldNameY(),
|
||||
finfo.precisionStep,
|
||||
bbox.getMinY(),
|
||||
bbox.getMaxY(),
|
||||
true,
|
||||
true);
|
||||
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
bq.add(qX,BooleanClause.Occur.MUST_NOT);
|
||||
bq.add(qY,BooleanClause.Occur.MUST_NOT);
|
||||
return bq;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
<!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.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Support for filtering based upon geographic location.
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Apache Lucene Spatial Strategies</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
[San Francisco] G5391959 @ IsWithin(-122.524918 37.674973 -122.360123 37.817108)
|
||||
[Wellington] G2179537 @ IsWithin(174.711456 -41.360779 174.854279 -41.213837)
|
||||
[Barcelona] G6544100 G3128760 @ IsWithin(2.127228 41.333313 2.226105 41.408844)
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
#id name shape
|
||||
FLK Falkland Is. -61.148055 -52.343055 -57.733200 -51.249455
|
||||
GUF French Guiana -54.603782 2.113473 -51.648055 5.755418
|
||||
GUY Guyana -61.389727 1.186873 -56.470636 8.535273
|
||||
PCN Pitcairn Is. -130.105055 -25.082227 -128.286118 -24.325836
|
||||
SGS South Georgia & the South Sandwich Is. -38.023755 -58.498609 -26.241391 -53.989727
|
||||
SHN St. Helena -5.792782 -16.021946 -5.645282 -15.903755
|
||||
SUR Suriname -58.071400 1.836245 -53.986118 6.001809
|
||||
TTO Trinidad & Tobago -61.921600 10.040345 -60.520836 11.345554
|
||||
VEN Venezuela -73.378064 0.649164 -59.803055 12.197500
|
||||
ASM American Samoa -170.823227 -14.375555 -170.561873 -14.254309
|
||||
COK Cook Is. -165.848345 -21.940836 -157.703764 -10.881318
|
||||
PYF French Polynesia -151.497773 -17.870836 -138.809755 -8.778191
|
||||
UMI Jarvis I. -160.045164 -0.398055 -160.009464 -0.374309
|
||||
NIU Niue -169.952236 -19.145555 -169.781555 -18.963336
|
||||
WSM Samoa -172.780027 -14.057500 -171.429200 -13.460555
|
||||
TKL Tokelau -171.862718 -9.218891 -171.843764 -9.170627
|
||||
TON Tonga -175.360000 -21.268064 -173.906827 -18.568055
|
||||
WLF Wallis & Futuna -178.190273 -14.323891 -176.121936 -13.214864
|
||||
ARG Argentina -73.582300 -55.051673 -53.650009 -21.780518
|
||||
BOL Bolivia -69.656191 -22.901109 -57.521118 -9.679191
|
||||
BRA Brazil -74.004591 -33.741118 -34.792918 5.272709
|
||||
CHL Chile -109.446109 -55.902227 -66.420627 -17.505282
|
||||
ECU Ecuador -91.663891 -5.000309 -75.216846 1.437782
|
||||
PRY Paraguay -62.643773 -27.584727 -54.243900 -19.296809
|
||||
PER Peru -81.355146 -18.348546 -68.673909 -0.036873
|
||||
URY Uruguay -58.438609 -34.943818 -53.098300 -30.096673
|
||||
UMI Baker I. -176.467655 0.215282 -176.455855 0.222573
|
||||
CAN Canada -141.002991 41.675554 -52.617364 83.113873
|
||||
GTM Guatemala -92.246782 13.745836 -88.214736 17.821109
|
||||
UMI Howland I. -176.643082 0.790282 -176.631091 0.808609
|
||||
UMI Johnston Atoll -169.538936 16.724164 -169.523927 16.730273
|
||||
MEX Mexico -118.404164 14.550545 -86.738618 32.718454
|
||||
UMI Midway Is. -177.395845 28.184154 -177.360545 28.221518
|
||||
BRB Barbados -59.659446 13.050554 -59.427082 13.337082
|
||||
DMA Dominica -61.491391 15.198054 -61.250700 15.631945
|
||||
GRD Grenada -61.785182 11.996945 -61.596391 12.237154
|
||||
GLP Guadeloupe -61.796109 15.870000 -61.187082 16.512918
|
||||
MTQ Martinique -61.231536 14.402773 -60.816946 14.880136
|
||||
LCA St. Lucia -61.079582 13.709445 -60.878064 14.109309
|
||||
SPM St. Pierre & Miquelon -56.397782 46.747191 -56.145500 47.135827
|
||||
VCT St. Vincent & the Grenadines -61.280146 13.130282 -61.120282 13.383191
|
||||
ABW Aruba -70.059664 12.411109 -69.874864 12.627773
|
||||
BMU Bermuda -64.823064 32.260554 -64.676809 32.379509
|
||||
DOM Dominican Republic -72.003064 17.604164 -68.322927 19.930827
|
||||
HTI Haiti -74.467791 18.022782 -71.629182 20.091454
|
||||
JAM Jamaica -78.373900 17.697218 -76.221118 18.522500
|
||||
ANT Netherlands Antilles -69.163618 12.020554 -68.192927 12.383891
|
||||
BHS The Bahamas -78.978900 20.915273 -72.738891 26.929164
|
||||
TCA Turks & Caicos Is. -72.031464 21.429918 -71.127573 21.957773
|
||||
BLZ Belize -89.216400 15.889854 -87.779591 18.489900
|
||||
CYM Cayman Is. -81.400836 19.265000 -81.093064 19.354164
|
||||
COL Colombia -81.720146 -4.236873 -66.870455 12.590273
|
||||
CRI Costa Rica -85.911391 8.025673 -82.561400 11.212845
|
||||
CUB Cuba -84.952927 19.821945 -74.131255 23.194027
|
||||
SLV El Salvador -90.108064 13.156391 -87.694673 14.431982
|
||||
HND Honduras -89.350491 12.985173 -83.131855 16.435827
|
||||
NIC Nicaragua -87.689827 10.709691 -83.131855 15.022218
|
||||
PAN Panama -83.030291 7.206109 -77.198336 9.620136
|
||||
AIA Anguilla -63.167782 18.164445 -62.972709 18.272982
|
||||
ATG Antigua & Barbuda -61.891109 16.989718 -61.666946 17.724300
|
||||
VGB British Virgin Is. -64.698482 18.383891 -64.324527 18.504854
|
||||
MSR Montserrat -62.236946 16.671391 -62.138891 16.812354
|
||||
PRI Puerto Rico -67.266400 17.922218 -65.301118 18.519445
|
||||
KNA St. Kitts & Nevis -62.862782 17.208882 -62.622509 17.410136
|
||||
VIR Virgin Is. -65.023509 17.676664 -64.562573 18.387673
|
||||
FRO Faroe Is. -7.433473 61.388327 -6.389718 62.357500
|
||||
GRL Greenland -73.053609 59.790273 -12.157637 83.623600
|
||||
XGK Guernsey -2.668609 49.422491 -2.500973 49.508191
|
||||
ISL Iceland -24.538400 63.390000 -13.499446 66.536100
|
||||
IRL Ireland -10.474727 51.445545 -6.013055 55.379991
|
||||
XIM Isle of Man -4.787155 54.055545 -4.308682 54.416382
|
||||
SJM Jan Mayen -9.119909 70.803863 -7.928509 71.180818
|
||||
XJE Jersey -2.247364 49.167773 -2.015000 49.261109
|
||||
GBR United Kingdom -8.171664 49.955273 1.749445 60.843327
|
||||
CPV Cape Verde -25.360555 14.811109 -22.666109 17.192364
|
||||
CIV Cote d'Ivoire -8.606382 4.344718 -2.487782 10.735254
|
||||
GHA Ghana -3.248891 4.727082 1.202782 11.155691
|
||||
GIB Gibraltar -5.356173 36.112073 -5.334509 36.163309
|
||||
LBR Liberia -11.492327 4.343609 -7.368400 8.512782
|
||||
MAR Morocco -13.174964 27.664236 -1.011809 35.919164
|
||||
PRT Portugal -31.289027 32.637500 -6.190455 42.150673
|
||||
ESP Spain -18.169864 27.637500 4.316945 43.764300
|
||||
ESH Western Sahara -17.101527 20.764100 -8.666391 27.666954
|
||||
BFA Burkina Faso -5.520837 9.395691 2.397927 15.082773
|
||||
GIN Guinea -15.080837 7.193927 -7.653373 12.677500
|
||||
GNB Guinea-Bissau -16.717773 10.925100 -13.643891 12.684718
|
||||
MLI Mali -12.244837 10.142154 4.251391 25.000273
|
||||
MRT Mauritania -17.075555 14.725636 -4.806109 27.290454
|
||||
SEN Senegal -17.532782 12.301745 -11.369927 16.690618
|
||||
SLE Sierra Leone -13.295609 6.923609 -10.264309 9.997500
|
||||
GMB The Gambia -16.821664 13.059973 -13.798609 13.826391
|
||||
DJI Djibouti 41.759854 10.942218 43.420409 12.708327
|
||||
ERI Eritrea 36.443282 12.363891 43.121382 17.994882
|
||||
ETH Ethiopia 32.991800 3.406664 47.988245 14.883609
|
||||
MNG Mongolia 87.761100 41.586654 119.931509 52.142773
|
||||
SDN Sudan 21.829100 3.493391 38.607500 22.232218
|
||||
UGA Uganda 29.574300 -1.476109 35.009718 4.222782
|
||||
ISR Gaza Strip 34.216663 31.216545 34.558891 31.596100
|
||||
IRQ Iraq 38.794700 29.061664 48.560691 37.383673
|
||||
ISR Israel 34.267582 29.486709 35.681109 33.270273
|
||||
JOR Jordan 34.960418 29.188891 39.301109 33.377591
|
||||
KAZ Kazakhstan 46.499163 40.594436 87.348209 55.442627
|
||||
NOR Norway 4.789582 57.987918 31.073536 71.154709
|
||||
RUS Russia -180.000000 41.196582 180.000000 81.851927
|
||||
SWE Sweden 11.113336 55.339164 24.167009 69.060300
|
||||
ISR West Bank 34.888191 31.350691 35.570609 32.546391
|
||||
DZA Algeria -8.667218 18.976391 11.986473 37.089854
|
||||
AND Andorra 1.421391 42.436382 1.781718 42.655964
|
||||
CMR Cameroon 8.502363 1.654164 16.207000 13.085000
|
||||
CAF Central African Republic 14.418891 2.221264 27.459718 11.000836
|
||||
LBY Libya 9.311391 19.499064 25.151663 33.171136
|
||||
MCO Monaco 7.390900 43.727545 7.439291 43.768300
|
||||
TUN Tunisia 7.492218 30.234391 11.581663 37.340409
|
||||
BEN Benin 0.776663 6.218718 3.855000 12.396654
|
||||
TCD Chad 13.461945 7.458536 24.002745 23.450554
|
||||
GNQ Equatorial Guinea 8.424163 0.930154 11.353891 3.763336
|
||||
KIR Kiribati -157.581700 1.335991 172.947509 2.033054
|
||||
NER Niger 0.166663 11.693273 15.996663 23.522309
|
||||
NGA Nigeria 2.692500 4.272845 14.649654 13.891500
|
||||
STP Sao Tome & Principe 6.465136 0.018336 7.463473 1.701245
|
||||
TGO Togo -0.149764 6.100545 1.797800 11.138536
|
||||
ALB Albania 19.288536 39.645000 21.053327 42.660345
|
||||
BIH Bosnia & Herzegovina 15.740591 42.565827 19.619782 45.265945
|
||||
HRV Croatia 13.504791 42.399991 19.425000 46.535827
|
||||
ITA Italy 6.623963 36.649164 18.514445 47.094582
|
||||
MKD Macedonia 20.458818 40.855891 23.030973 42.358954
|
||||
MLT Malta 14.329100 35.800000 14.570000 35.991936
|
||||
SMR San Marino 12.406945 43.898682 12.511109 43.986873
|
||||
SMN Serbia & Montenegro 18.453327 41.849000 23.005000 46.181109
|
||||
VTC Vatican City 12.444473 41.900891 12.457718 41.908391
|
||||
BGR Bulgaria 22.365273 41.243045 28.605136 44.224718
|
||||
CYP Cyprus 32.269863 34.640273 34.586036 35.688609
|
||||
EGY Egypt 24.706800 21.994164 36.895827 31.646945
|
||||
GEO Georgia 40.002963 41.048045 46.710818 43.584718
|
||||
GRC Greece 19.640000 34.930545 28.238045 41.747773
|
||||
LBN Lebanon 35.100827 33.062082 36.623745 34.647500
|
||||
SYR Syria 35.614463 32.313609 42.378327 37.290545
|
||||
TUR Turkey 25.665827 35.818445 44.820545 42.109991
|
||||
AUT Austria 9.533573 46.407491 17.166382 49.018745
|
||||
CZE Czech Republic 12.093700 48.581382 18.852218 51.052491
|
||||
DNK Denmark 8.092918 54.561936 15.149163 57.745973
|
||||
HUN Hungary 16.111800 45.748327 22.894800 48.576173
|
||||
POL Poland 14.147636 49.002918 24.143473 54.836036
|
||||
SVK Slovakia 16.844718 47.737500 22.558054 49.600827
|
||||
SVN Slovenia 13.383473 45.425818 16.607873 46.876245
|
||||
SJM Svalbard 10.487918 74.343045 33.637500 80.764163
|
||||
BEL Belgium 2.541663 49.508882 6.398200 51.501245
|
||||
FRA France -4.790282 41.364927 9.562218 51.091109
|
||||
DEU Germany 5.865000 47.274718 15.033818 55.056527
|
||||
LIE Liechtenstein 9.474636 47.057454 9.633891 47.274545
|
||||
LUX Luxembourg 5.734445 49.448464 6.524027 50.181809
|
||||
NLD Netherlands 3.370863 50.753882 7.210973 53.465827
|
||||
CHE Switzerland 5.967009 45.829436 10.488209 47.806664
|
||||
USA United States -178.216555 18.925482 179.775936 71.351436
|
||||
BLR Belarus 23.165400 51.251845 32.740054 56.167491
|
||||
EST Estonia 21.837354 57.522636 28.194091 59.664718
|
||||
FIN Finland 19.511391 59.806800 31.581963 70.088609
|
||||
LVA Latvia 20.968609 55.674836 28.235963 58.083254
|
||||
LTU Lithuania 20.942836 53.890336 26.813054 56.449854
|
||||
MDA Moldova 26.634991 45.448645 30.128709 48.468318
|
||||
ROM Romania 20.261027 43.623309 29.672218 48.263882
|
||||
UKR Ukraine 22.151445 44.379154 40.178745 52.378600
|
||||
IND India 68.144227 6.745827 97.380536 35.505618
|
||||
MDV Maldives 72.863391 -0.641664 73.637272 7.027773
|
||||
OMN Oman 51.999291 16.642782 59.847082 26.368709
|
||||
SOM Somalia 40.988609 -1.674873 51.411318 11.979164
|
||||
LKA Sri Lanka 79.696091 5.918054 81.891663 9.828191
|
||||
TKM Turkmenistan 51.250182 35.145991 66.670882 42.796173
|
||||
UZB Uzbekistan 55.997491 37.184991 73.167545 45.570591
|
||||
YEM Yemen 42.555973 12.144718 54.473473 18.999345
|
||||
ARM Armenia 43.454163 38.841145 46.620536 41.297054
|
||||
AZE Azerbaijan 44.778863 38.262809 51.677009 42.710754
|
||||
BHR Bahrain 50.453327 25.571945 50.796391 26.288891
|
||||
IRN Iran 44.034954 25.075973 63.330273 39.779154
|
||||
KWT Kuwait 46.546945 28.538882 48.416591 30.084164
|
||||
QAT Qatar 50.751936 24.556045 51.615827 26.152500
|
||||
SAU Saudi Arabia 34.572145 16.377500 55.666109 32.154945
|
||||
ARE United Arab Emirates 51.583327 22.633327 56.381663 26.083882
|
||||
AFG Afghanistan 60.504163 29.406109 74.915736 38.471982
|
||||
KGZ Kyrgyzstan 69.249500 39.195473 80.281582 43.216900
|
||||
NPL Nepal 80.052200 26.368364 88.194554 30.424718
|
||||
PAK Pakistan 60.866300 23.688045 77.823927 37.060791
|
||||
TJK Tajikistan 67.364700 36.671845 75.187482 41.049254
|
||||
BGD Bangladesh 88.043872 20.744818 92.669345 26.626136
|
||||
BTN Bhutan 88.751936 26.703609 92.114218 28.325000
|
||||
BRN Brunei 114.095082 4.018191 115.360263 5.053054
|
||||
CHN China 73.620045 18.168882 134.768463 53.553745
|
||||
JPN Japan 123.678863 24.251391 145.812409 45.486382
|
||||
PRK North Korea 124.323954 37.671382 130.697418 43.006100
|
||||
PLW Palau 134.452482 7.305254 134.658872 7.729445
|
||||
PHL Philippines 116.950000 5.049164 126.598036 19.391109
|
||||
KOR South Korea 126.099018 33.192209 129.586872 38.625245
|
||||
KHM Cambodia 102.346509 10.422736 107.636382 14.708618
|
||||
LAO Laos 100.091372 13.926664 107.695254 22.499927
|
||||
MYS Malaysia 99.641936 0.852782 119.275818 7.352918
|
||||
MMR Myanmar 92.204991 9.839582 101.169427 28.546527
|
||||
SGP Singapore 103.640945 1.259027 103.997945 1.445282
|
||||
THA Thailand 97.347272 5.633473 105.639291 20.454582
|
||||
VNM Vietnam 102.140745 8.559236 109.464845 23.324164
|
||||
GUM Guam 144.634154 13.235000 144.953309 13.652291
|
||||
MHL Marshall Is. 162.324963 5.600273 171.378063 14.594027
|
||||
FSM Micronesia 158.120100 5.261664 163.042891 6.977636
|
||||
MNP Northern Mariana Is. 145.572682 14.908054 145.818082 15.268191
|
||||
UMI Wake I. 166.608981 19.279445 166.662200 19.324582
|
||||
BWA Botswana 19.996109 -26.875555 29.373618 -17.782082
|
||||
BDI Burundi 28.985000 -4.448055 30.853191 -2.301564
|
||||
ATF French Southern & Antarctic Lands 51.650836 -49.725009 70.567491 -46.327645
|
||||
HMD Heard I. & McDonald Is. 73.234709 -53.199445 73.773882 -52.965145
|
||||
KEN Kenya 33.907218 -4.669618 41.905163 4.622500
|
||||
RWA Rwanda 28.854445 -2.825491 30.893263 -1.054446
|
||||
TZA Tanzania 29.340827 -11.740418 40.436809 -0.997218
|
||||
ZMB Zambia 21.996391 -18.074918 33.702282 -8.191664
|
||||
ZWE Zimbabwe 25.237918 -22.414764 33.071591 -15.616527
|
||||
ATA Antarctica -180.000000 -90.000000 180.000000 -60.503336
|
||||
NOR Bouvet I. 3.342363 -54.462782 3.484163 -54.383609
|
||||
COM Comoros 43.214027 -12.383055 44.530418 -11.366946
|
||||
REU Juan De Nova I. 42.723818 -17.076118 42.760900 -17.052018
|
||||
LSO Lesotho 27.013973 -30.650527 29.455554 -28.570691
|
||||
MWI Malawi 32.681873 -17.135282 35.920963 -9.376673
|
||||
MOZ Mozambique 30.213018 -26.860282 40.846109 -10.471109
|
||||
ZAF South Africa 16.483327 -46.969727 37.892218 -22.136391
|
||||
SWZ Swaziland 30.798336 -27.316391 32.133400 -25.728336
|
||||
AGO Angola 11.731245 -18.016391 24.084445 -4.388991
|
||||
COG Congo 11.140663 -5.015000 18.643609 3.711109
|
||||
ZAR Congo, DRC 12.214554 -13.458055 31.302773 5.380691
|
||||
FJI Fiji -180.000000 -19.162782 180.000000 -16.153473
|
||||
GAB Gabon 8.700836 -3.925282 14.519582 2.317900
|
||||
NAM Namibia 11.716391 -28.961873 25.264427 -16.954173
|
||||
NZL New Zealand -176.848755 -52.578055 178.841063 -34.414718
|
||||
IOT British Indian Ocean Territory 72.357900 -7.436246 72.494282 -7.233473
|
||||
REU Glorioso Is. 47.279091 -11.577782 47.303054 -11.554100
|
||||
MDG Madagascar 43.236827 -25.588336 50.501391 -11.945555
|
||||
MUS Mauritius 57.306309 -20.520555 63.495754 -19.673336
|
||||
MYT Mayotte 45.039163 -12.992500 45.293345 -12.662500
|
||||
REU Reunion 55.220554 -21.373891 55.853054 -20.856527
|
||||
SYC Seychelles 46.205691 -9.463055 55.540554 -4.551664
|
||||
CXR Christmas I. 105.629000 -10.510973 105.751900 -10.384082
|
||||
CCK Cocos Is. 96.817491 -12.199446 96.864845 -12.130418
|
||||
IDN Indonesia 95.210945 -10.929655 141.007018 5.913473
|
||||
TLS Timor Leste 124.046100 -9.463627 127.308591 -8.140000
|
||||
AUS Australia 112.907209 -54.753891 158.960372 -10.135691
|
||||
NRU Nauru 166.904418 -0.552218 166.957045 -0.493336
|
||||
NCL New Caledonia 163.982745 -22.673891 168.130509 -20.087918
|
||||
NFK Norfolk I. 167.910945 -29.081109 167.998872 -29.000555
|
||||
PNG Papua New Guinea 140.858854 -11.642500 155.966845 -1.355282
|
||||
SLB Solomon Is. 155.671300 -11.845836 166.931836 -6.605518
|
||||
TUV Tuvalu 176.295254 -8.561291 179.232281 -6.089446
|
||||
VUT Vanuatu 166.521636 -20.254173 169.893863 -13.707218
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
#id name shape
|
||||
HI Hawaii -160.242406 18.921786 -154.791096 22.229120
|
||||
WA Washington -124.732769 45.543092 -116.919132 48.999931
|
||||
MT Montana -116.063531 44.353639 -104.043072 49.000026
|
||||
ME Maine -71.087509 43.091050 -66.969271 47.453334
|
||||
ND North Dakota -104.062991 45.930822 -96.551931 49.000026
|
||||
SD South Dakota -104.061036 42.488459 -96.439394 45.943547
|
||||
WY Wyoming -111.053428 40.994289 -104.051705 45.002793
|
||||
WI Wisconsin -92.885397 42.489152 -86.967712 46.952479
|
||||
ID Idaho -117.236921 41.994599 -111.046771 48.999950
|
||||
VT Vermont -73.436000 42.725852 -71.505372 45.013351
|
||||
MN Minnesota -97.229436 43.498102 -89.530673 49.371730
|
||||
OR Oregon -124.559617 41.987672 -116.470418 46.236091
|
||||
NH New Hampshire -72.553428 42.698603 -70.734139 45.301469
|
||||
IA Iowa -96.640709 40.371946 -90.142796 43.501457
|
||||
MA Massachusetts -73.498840 41.238279 -69.917780 42.886877
|
||||
NE Nebraska -104.056219 39.992595 -95.308697 43.003062
|
||||
NY New York -79.763235 40.506003 -71.869986 45.006138
|
||||
PA Pennsylvania -80.526045 39.719313 -74.700062 42.267327
|
||||
CT Connecticut -73.725237 40.998392 -71.788249 42.047428
|
||||
RI Rhode Island -71.866678 41.322769 -71.117132 42.013713
|
||||
NJ New Jersey -75.570234 38.956682 -73.896148 41.350573
|
||||
IN Indiana -88.101490 37.776224 -84.787446 41.765540
|
||||
NV Nevada -119.996324 34.998914 -114.037392 41.996637
|
||||
UT Utah -114.047273 36.991746 -109.043206 42.002300
|
||||
CA California -124.392638 32.535781 -114.125230 42.002191
|
||||
OH Ohio -84.812070 38.400511 -80.519996 41.986872
|
||||
IL Illinois -91.516284 36.986822 -87.507909 42.509363
|
||||
DC District of Columbia -77.122328 38.788234 -76.910904 38.993541
|
||||
DE Delaware -75.791094 38.449602 -75.045623 39.840119
|
||||
WV West Virginia -82.647158 37.204910 -77.727467 40.637203
|
||||
MD Maryland -79.489865 37.970255 -75.045623 39.725461
|
||||
CO Colorado -109.055861 36.988994 -102.037207 41.003375
|
||||
KY Kentucky -89.568231 36.496570 -81.959575 39.142063
|
||||
KS Kansas -102.051535 36.988875 -94.601224 40.002987
|
||||
VA Virginia -83.675177 36.541623 -75.242219 39.456998
|
||||
MO Missouri -95.767479 35.989656 -89.105034 40.609784
|
||||
AZ Arizona -114.821761 31.335634 -109.045615 37.003926
|
||||
OK Oklahoma -102.997709 33.621136 -94.428552 37.001478
|
||||
NC North Carolina -84.323773 33.882164 -75.456580 36.589767
|
||||
TN Tennessee -90.305448 34.988759 -81.652272 36.679683
|
||||
TX Texas -106.650062 25.845557 -93.507389 36.493912
|
||||
NM New Mexico -109.051346 31.343453 -102.997401 36.999760
|
||||
AL Alabama -88.472952 30.233604 -84.894016 35.016033
|
||||
MS Mississippi -91.643682 30.194935 -88.090468 35.005041
|
||||
GA Georgia -85.608960 30.361291 -80.894753 35.000366
|
||||
SC South Carolina -83.350685 32.068173 -78.579453 35.208356
|
||||
AR Arkansas -94.617257 33.010151 -89.645479 36.492811
|
||||
LA Louisiana -94.041785 28.939655 -89.021803 33.023422
|
||||
FL Florida -87.625711 24.956376 -80.050911 31.003157
|
||||
MI Michigan -90.408200 41.697494 -82.419836 48.173795
|
||||
AK Alaska -178.217598 51.583032 -129.992235 71.406235
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
WY CO @ Intersects(-106.964844 39.460938 -105.734375 42.800781)
|
||||
TX @ Intersects(-99.669922 30.583984 -98.439453 32.253906)
|
||||
MS TX LA @ Intersects(-95.363281 29.792969 -90.133789 32.473633)
|
|
@ -0,0 +1,4 @@
|
|||
KS @ IsWithin(-103.493164 36.208984 -93.825195 41.086914)
|
||||
WA @ IsWithin(-126.916016 44.36084 -115.314453 50.688965)
|
||||
MA CT RI @ IsWithin(-73.894043 40.825195 -69.521484 43.198242)
|
||||
AL GA @ IsWithin(-89.472656 29.311523 -80.244141 35.90332)
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
public class SpatialMatchConcern {
|
||||
public final boolean orderIsImportant;
|
||||
public final boolean resultsAreSuperset; // if the strategy can not give exact answers, but used to limit results
|
||||
|
||||
private SpatialMatchConcern( boolean order, boolean superset ) {
|
||||
this.orderIsImportant = order;
|
||||
this.resultsAreSuperset = superset;
|
||||
}
|
||||
|
||||
public static final SpatialMatchConcern EXACT = new SpatialMatchConcern( true, false );
|
||||
public static final SpatialMatchConcern FILTER = new SpatialMatchConcern( false, false );
|
||||
public static final SpatialMatchConcern SUPERSET = new SpatialMatchConcern( false, true );
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SpatialTestCase extends LuceneTestCase {
|
||||
|
||||
private DirectoryReader indexReader;
|
||||
private IndexWriter indexWriter;
|
||||
private Directory directory;
|
||||
private IndexSearcher indexSearcher;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
directory = newDirectory();
|
||||
|
||||
IndexWriterConfig writerConfig = newIndexWriterConfig(random, TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT));
|
||||
indexWriter = new IndexWriter(directory, writerConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (indexWriter != null) {
|
||||
indexWriter.close();
|
||||
}
|
||||
if (indexReader != null) {
|
||||
indexReader.close();
|
||||
}
|
||||
if (directory != null) {
|
||||
directory.close();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
// ================================================= Helper Methods ================================================
|
||||
|
||||
protected void addDocument(Document doc) throws IOException {
|
||||
indexWriter.addDocument(doc);
|
||||
}
|
||||
|
||||
protected void addDocumentsAndCommit(List<Document> documents) throws IOException {
|
||||
for (Document document : documents) {
|
||||
indexWriter.addDocument(document);
|
||||
}
|
||||
commit();
|
||||
}
|
||||
|
||||
protected void deleteAll() throws IOException {
|
||||
indexWriter.deleteAll();
|
||||
}
|
||||
|
||||
protected void commit() throws IOException {
|
||||
indexWriter.commit();
|
||||
if (indexReader == null) {
|
||||
indexReader = DirectoryReader.open(directory);
|
||||
} else {
|
||||
indexReader = DirectoryReader.openIfChanged(indexReader);
|
||||
}
|
||||
indexSearcher = newSearcher(indexReader);
|
||||
}
|
||||
|
||||
protected void verifyDocumentsIndexed(int numDocs) {
|
||||
assertEquals(numDocs, indexReader.numDocs());
|
||||
}
|
||||
|
||||
protected SearchResults executeQuery(Query query, int numDocs) {
|
||||
try {
|
||||
TopDocs topDocs = indexSearcher.search(query, numDocs);
|
||||
|
||||
List<SearchResult> results = new ArrayList<SearchResult>();
|
||||
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
|
||||
results.add(new SearchResult(scoreDoc.score, indexSearcher.doc(scoreDoc.doc)));
|
||||
}
|
||||
return new SearchResults(topDocs.totalHits, results);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("IOException thrown while executing query", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================= Inner Classes =================================================
|
||||
|
||||
protected static class SearchResults {
|
||||
|
||||
public int numFound;
|
||||
public List<SearchResult> results;
|
||||
|
||||
public SearchResults(int numFound, List<SearchResult> results) {
|
||||
this.numFound = numFound;
|
||||
this.results = results;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class SearchResult {
|
||||
|
||||
public float score;
|
||||
public Document document;
|
||||
|
||||
public SearchResult(float score, Document document) {
|
||||
this.score = score;
|
||||
this.document = document;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.io.LineReader;
|
||||
import com.spatial4j.core.query.SpatialArgs;
|
||||
import com.spatial4j.core.query.SpatialArgsParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Helper class to execute queries
|
||||
*/
|
||||
public class SpatialTestQuery {
|
||||
public String testname;
|
||||
public String line;
|
||||
public int lineNumber = -1;
|
||||
public SpatialArgs args;
|
||||
public List<String> ids = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Get Test Queries
|
||||
*/
|
||||
public static Iterator<SpatialTestQuery> getTestQueries(
|
||||
final SpatialArgsParser parser,
|
||||
final SpatialContext ctx,
|
||||
final String name,
|
||||
final InputStream in ) throws IOException {
|
||||
return new LineReader<SpatialTestQuery>(new InputStreamReader(in,"UTF-8")) {
|
||||
|
||||
@Override
|
||||
public SpatialTestQuery parseLine(String line) {
|
||||
SpatialTestQuery test = new SpatialTestQuery();
|
||||
test.line = line;
|
||||
test.lineNumber = getLineNumber();
|
||||
|
||||
try {
|
||||
// skip a comment
|
||||
if( line.startsWith( "[" ) ) {
|
||||
int idx = line.indexOf( ']' );
|
||||
if( idx > 0 ) {
|
||||
line = line.substring( idx+1 );
|
||||
}
|
||||
}
|
||||
|
||||
int idx = line.indexOf('@');
|
||||
StringTokenizer st = new StringTokenizer(line.substring(0, idx));
|
||||
while (st.hasMoreTokens()) {
|
||||
test.ids.add(st.nextToken().trim());
|
||||
}
|
||||
test.args = parser.parse(line.substring(idx + 1).trim(), ctx);
|
||||
return test;
|
||||
}
|
||||
catch( Exception ex ) {
|
||||
throw new RuntimeException( "invalid query line: "+test.line, ex );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return line;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.io.sample.SampleData;
|
||||
import com.spatial4j.core.io.sample.SampleDataReader;
|
||||
import com.spatial4j.core.query.SpatialArgsParser;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.junit.Assert;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class StrategyTestCase<T extends SpatialFieldInfo> extends SpatialTestCase {
|
||||
|
||||
public static final String DATA_STATES_POLY = "states-poly.txt";
|
||||
public static final String DATA_STATES_BBOX = "states-bbox.txt";
|
||||
public static final String DATA_COUNTRIES_POLY = "countries-poly.txt";
|
||||
public static final String DATA_COUNTRIES_BBOX = "countries-bbox.txt";
|
||||
public static final String DATA_WORLD_CITIES_POINTS = "world-cities-points.txt";
|
||||
|
||||
public static final String QTEST_States_IsWithin_BBox = "states-IsWithin-BBox.txt";
|
||||
public static final String QTEST_States_Intersects_BBox = "states-Intersects-BBox.txt";
|
||||
|
||||
public static final String QTEST_Cities_IsWithin_BBox = "cities-IsWithin-BBox.txt";
|
||||
|
||||
private Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
protected final SpatialArgsParser argsParser = new SpatialArgsParser();
|
||||
|
||||
protected SpatialStrategy<T> strategy;
|
||||
protected SpatialContext ctx;
|
||||
protected T fieldInfo;
|
||||
protected boolean storeShape = true;
|
||||
|
||||
protected void executeQueries(SpatialMatchConcern concern, String... testQueryFile) throws IOException {
|
||||
log.info("testing queried for strategy "+strategy);
|
||||
for( String path : testQueryFile ) {
|
||||
Iterator<SpatialTestQuery> testQueryIterator = getTestQueries(path, ctx);
|
||||
runTestQueries(testQueryIterator, concern);
|
||||
}
|
||||
}
|
||||
|
||||
protected void getAddAndVerifyIndexedDocuments(String testDataFile) throws IOException {
|
||||
List<Document> testDocuments = getDocuments(testDataFile);
|
||||
addDocumentsAndCommit(testDocuments);
|
||||
verifyDocumentsIndexed(testDocuments.size());
|
||||
}
|
||||
|
||||
protected List<Document> getDocuments(String testDataFile) throws IOException {
|
||||
Iterator<SampleData> sampleData = getSampleData(testDataFile);
|
||||
List<Document> documents = new ArrayList<Document>();
|
||||
while (sampleData.hasNext()) {
|
||||
SampleData data = sampleData.next();
|
||||
Document document = new Document();
|
||||
document.add(new Field("id", data.id, StringField.TYPE_STORED));
|
||||
document.add(new Field("name", data.name, StringField.TYPE_STORED));
|
||||
Shape shape = ctx.readShape(data.shape);
|
||||
for (IndexableField f : strategy.createFields(fieldInfo, shape, true, storeShape)) {
|
||||
if( f != null ) { // null if incompatibleGeometry && ignore
|
||||
document.add(f);
|
||||
}
|
||||
}
|
||||
documents.add(document);
|
||||
}
|
||||
return documents;
|
||||
}
|
||||
|
||||
protected Iterator<SampleData> getSampleData(String testDataFile) throws IOException {
|
||||
return new SampleDataReader(
|
||||
getClass().getClassLoader().getResourceAsStream("data/"+testDataFile) );
|
||||
}
|
||||
|
||||
protected Iterator<SpatialTestQuery> getTestQueries(String testQueryFile, SpatialContext ctx) throws IOException {
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream(testQueryFile);
|
||||
return SpatialTestQuery.getTestQueries(
|
||||
argsParser, ctx, testQueryFile, in );
|
||||
}
|
||||
|
||||
public void runTestQueries(
|
||||
Iterator<SpatialTestQuery> queries,
|
||||
SpatialMatchConcern concern) {
|
||||
while (queries.hasNext()) {
|
||||
SpatialTestQuery q = queries.next();
|
||||
|
||||
String msg = q.line; //"Query: " + q.args.toString(ctx);
|
||||
SearchResults got = executeQuery(strategy.makeQuery(q.args, fieldInfo), 100);
|
||||
if (concern.orderIsImportant) {
|
||||
Iterator<String> ids = q.ids.iterator();
|
||||
for (SearchResult r : got.results) {
|
||||
String id = r.document.get("id");
|
||||
Assert.assertEquals( "out of order: " + msg, ids.next(), id);
|
||||
}
|
||||
if (ids.hasNext()) {
|
||||
Assert.fail(msg + " :: expect more results then we got: " + ids.next());
|
||||
}
|
||||
} else {
|
||||
// We are looking at how the results overlap
|
||||
if( concern.resultsAreSuperset ) {
|
||||
Set<String> found = new HashSet<String>();
|
||||
for (SearchResult r : got.results) {
|
||||
found.add(r.document.get("id"));
|
||||
}
|
||||
for( String s : q.ids ) {
|
||||
if( !found.contains( s ) ) {
|
||||
Assert.fail( "Results are mising id: "+s + " :: " + found );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
List<String> found = new ArrayList<String>();
|
||||
for (SearchResult r : got.results) {
|
||||
found.add(r.document.get("id"));
|
||||
}
|
||||
|
||||
// sort both so that the order is not important
|
||||
Collections.sort(q.ids);
|
||||
Collections.sort(found);
|
||||
Assert.assertEquals(msg, q.ids.toString(), found.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
import com.spatial4j.core.query.SpatialArgsParser;
|
||||
import com.spatial4j.core.query.SpatialOperation;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Make sure we are reading the tests as expected
|
||||
*/
|
||||
public class TestTestFramework {
|
||||
|
||||
@Test
|
||||
public void testQueries() throws IOException {
|
||||
String name = StrategyTestCase.QTEST_Cities_IsWithin_BBox;
|
||||
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream(name);
|
||||
SpatialContext ctx = SimpleSpatialContext.GEO_KM;
|
||||
Iterator<SpatialTestQuery> iter = SpatialTestQuery.getTestQueries(
|
||||
new SpatialArgsParser(), ctx, name, in );
|
||||
List<SpatialTestQuery> tests = new ArrayList<SpatialTestQuery>();
|
||||
while( iter.hasNext() ) {
|
||||
tests.add( iter.next() );
|
||||
}
|
||||
Assert.assertEquals( 3, tests.size() );
|
||||
|
||||
SpatialTestQuery sf = tests.get(0);
|
||||
// assert
|
||||
Assert.assertEquals( 1, sf.ids.size() );
|
||||
Assert.assertTrue( sf.ids.get(0).equals( "G5391959" ) );
|
||||
Assert.assertTrue( sf.args.getShape() instanceof Rectangle);
|
||||
Assert.assertEquals( SpatialOperation.IsWithin, sf.args.getOperation() );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import org.apache.lucene.spatial.SimpleSpatialFieldInfo;
|
||||
import org.apache.lucene.spatial.SpatialMatchConcern;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public abstract class BaseRecursivePrefixTreeStrategyTestCase extends StrategyTestCase<SimpleSpatialFieldInfo> {
|
||||
|
||||
private int maxLength;
|
||||
|
||||
protected abstract SpatialContext getSpatialContext();
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
maxLength = GeohashPrefixTree.getMaxLevelsPossible();
|
||||
// SimpleIO
|
||||
this.ctx = getSpatialContext();
|
||||
this.strategy = new RecursivePrefixTreeStrategy(new GeohashPrefixTree(
|
||||
ctx, maxLength ));
|
||||
this.fieldInfo = new SimpleSpatialFieldInfo( getClass().getSimpleName() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterWithVariableScanLevel() throws IOException {
|
||||
getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
|
||||
|
||||
//execute queries for each prefix grid scan level
|
||||
for(int i = 0; i <= maxLength; i++) {
|
||||
((RecursivePrefixTreeStrategy)strategy).setPrefixGridScanLevel(i);
|
||||
executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
import org.junit.Before;
|
||||
|
||||
|
||||
public class RecursivePrefixTreeStrategyTestCase extends BaseRecursivePrefixTreeStrategyTestCase {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpatialContext getSpatialContext() {
|
||||
return SimpleSpatialContext.GEO_KM;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* This is just a quick idea for *simple* tests
|
||||
*/
|
||||
public class TestSpatialPrefixField {
|
||||
|
||||
@Test
|
||||
public void testRawTokens() {
|
||||
// Ignoring geometry for now, and focus on what tokens need to match
|
||||
|
||||
List<String> docA = Arrays.asList(
|
||||
"AAAAAA*",
|
||||
"AAAAAB+"
|
||||
);
|
||||
|
||||
List<String> docB = Arrays.asList(
|
||||
"A*",
|
||||
"BB*"
|
||||
);
|
||||
|
||||
// Assumptions:
|
||||
checkQuery("AAAAA", "docA", "docB");
|
||||
checkQuery("AAAAA*", "docA", "docB"); // for now * and + are essentially identical
|
||||
checkQuery("AAAAA+", "docA", "docB"); // down the road, there may be a difference between 'covers' and an edge
|
||||
|
||||
checkQuery("AA*", "docB", "docA"); // Bigger input query
|
||||
|
||||
checkQuery("AAAAAAAAAAAA*", "docA", "docB"); // small
|
||||
|
||||
checkQuery("BC"); // nothing
|
||||
checkQuery("XX"); // nothing
|
||||
|
||||
// match only B
|
||||
checkQuery("B", "docB");
|
||||
checkQuery("BBBB", "docB");
|
||||
checkQuery("B*", "docB");
|
||||
checkQuery("BBBB*", "docB");
|
||||
}
|
||||
|
||||
void checkQuery(String query, String... expect) {
|
||||
// TODO, check that the query returns the docs in order
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
import com.spatial4j.core.query.SpatialArgsParser;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.simple.PointImpl;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.spatial.SimpleSpatialFieldInfo;
|
||||
import org.apache.lucene.spatial.SpatialTestCase;
|
||||
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class TestTermQueryPrefixGridStrategy extends SpatialTestCase {
|
||||
|
||||
@Test
|
||||
public void testNGramPrefixGridLosAngeles() throws IOException {
|
||||
SimpleSpatialFieldInfo fieldInfo = new SimpleSpatialFieldInfo("geo");
|
||||
SpatialContext ctx = SimpleSpatialContext.GEO_KM;
|
||||
TermQueryPrefixTreeStrategy prefixGridStrategy = new TermQueryPrefixTreeStrategy(new QuadPrefixTree(ctx));
|
||||
|
||||
Shape point = new PointImpl(-118.243680, 34.052230);
|
||||
|
||||
Document losAngeles = new Document();
|
||||
losAngeles.add(new Field("name", "Los Angeles", StringField.TYPE_STORED));
|
||||
losAngeles.add(prefixGridStrategy.createField(fieldInfo, point, true, true));
|
||||
|
||||
addDocumentsAndCommit(Arrays.asList(losAngeles));
|
||||
|
||||
// This won't work with simple spatial context...
|
||||
SpatialArgsParser spatialArgsParser = new SpatialArgsParser();
|
||||
// TODO... use a non polygon query
|
||||
// SpatialArgs spatialArgs = spatialArgsParser.parse(
|
||||
// "IsWithin(POLYGON((-127.00390625 39.8125,-112.765625 39.98828125,-111.53515625 31.375,-125.94921875 30.14453125,-127.00390625 39.8125)))",
|
||||
// new SimpleSpatialContext());
|
||||
|
||||
// Query query = prefixGridStrategy.makeQuery(spatialArgs, fieldInfo);
|
||||
// SearchResults searchResults = executeQuery(query, 1);
|
||||
// assertEquals(1, searchResults.numFound);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.prefix.tree;
|
||||
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class SpatialPrefixTreeTest {
|
||||
|
||||
//TODO plug in others and test them
|
||||
private SimpleSpatialContext ctx;
|
||||
private SpatialPrefixTree trie;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ctx = SimpleSpatialContext.GEO_KM;
|
||||
trie = new GeohashPrefixTree(ctx,4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeTraverse() {
|
||||
Node prevN = null;
|
||||
Node n = trie.getWorldNode();
|
||||
assertEquals(0,n.getLevel());
|
||||
assertEquals(ctx.getWorldBounds(),n.getShape());
|
||||
while(n.getLevel() < trie.getMaxLevels()) {
|
||||
prevN = n;
|
||||
n = n.getSubCells().iterator().next();//TODO random which one?
|
||||
|
||||
assertEquals(prevN.getLevel()+1,n.getLevel());
|
||||
Rectangle prevNShape = (Rectangle) prevN.getShape();
|
||||
Shape s = n.getShape();
|
||||
Rectangle sbox = s.getBoundingBox();
|
||||
assertTrue(prevNShape.getWidth() > sbox.getWidth());
|
||||
assertTrue(prevNShape.getHeight() > sbox.getHeight());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import org.apache.lucene.search.FieldCache;
|
||||
import org.apache.lucene.spatial.SpatialMatchConcern;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
import org.apache.lucene.spatial.util.NumericFieldInfo;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public abstract class BaseTwoDoublesStrategyTestCase extends StrategyTestCase<TwoDoublesFieldInfo> {
|
||||
|
||||
protected abstract SpatialContext getSpatialContext();
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.ctx = getSpatialContext();
|
||||
this.strategy = new TwoDoublesStrategy(ctx,
|
||||
new NumericFieldInfo(), FieldCache.NUMERIC_UTILS_DOUBLE_PARSER);
|
||||
this.fieldInfo = new TwoDoublesFieldInfo(getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCitiesWithinBBox() throws IOException {
|
||||
getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
|
||||
executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
|
||||
public class TwoDoublesStrategyTestCase extends BaseTwoDoublesStrategyTestCase {
|
||||
|
||||
@Override
|
||||
protected SpatialContext getSpatialContext() {
|
||||
return SimpleSpatialContext.GEO_KM;
|
||||
}
|
||||
}
|
|
@ -22,9 +22,12 @@ import org.apache.lucene.queries.function.valuesource.LiteralValueSource;
|
|||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.spatial.geohash.GeoHashUtils;
|
||||
import org.apache.lucene.spatial.DistanceUtils;
|
||||
import org.apache.lucene.spatial.tier.InvalidGeoException;
|
||||
import com.spatial4j.core.context.ParseUtils;
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||
import com.spatial4j.core.exception.InvalidShapeException;
|
||||
import com.spatial4j.core.util.GeohashUtils;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.response.TextResponseWriter;
|
||||
import org.apache.solr.search.QParser;
|
||||
|
@ -41,11 +44,13 @@ import java.io.IOException;
|
|||
* href="http://en.wikipedia.org/wiki/Geohash">Geohash</a> field. The field is
|
||||
* provided as a lat/lon pair and is internally represented as a string.
|
||||
*
|
||||
* @see org.apache.lucene.spatial.DistanceUtils#parseLatitudeLongitude(double[], String)
|
||||
* @see com.spatial4j.core.context.ParseUtils#parseLatitudeLongitude(double[], String)
|
||||
*/
|
||||
public class GeoHashField extends FieldType implements SpatialQueryable {
|
||||
|
||||
|
||||
private final SpatialContext ctx = SimpleSpatialContext.GEO_KM;
|
||||
|
||||
@Override
|
||||
public SortField getSortField(SchemaField field, boolean top) {
|
||||
return getStringSort(field, top);
|
||||
|
@ -57,11 +62,11 @@ public class GeoHashField extends FieldType implements SpatialQueryable {
|
|||
public Query createSpatialQuery(QParser parser, SpatialOptions options) {
|
||||
double [] point = new double[0];
|
||||
try {
|
||||
point = DistanceUtils.parsePointDouble(null, options.pointStr, 2);
|
||||
} catch (InvalidGeoException e) {
|
||||
point = ParseUtils.parsePointDouble(null, options.pointStr, 2);
|
||||
} catch (InvalidShapeException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
|
||||
}
|
||||
String geohash = GeoHashUtils.encode(point[0], point[1]);
|
||||
String geohash = GeohashUtils.encodeLatLon(point[0], point[1]);
|
||||
//TODO: optimize this
|
||||
return new SolrConstantScoreQuery(new ValueSourceRangeFilter(new GeohashHaversineFunction(getValueSource(options.field, parser),
|
||||
new LiteralValueSource(geohash), options.radius), "0", String.valueOf(options.distance), true, true));
|
||||
|
@ -76,8 +81,8 @@ public class GeoHashField extends FieldType implements SpatialQueryable {
|
|||
|
||||
@Override
|
||||
public String toExternal(IndexableField f) {
|
||||
double[] latLon = GeoHashUtils.decode(f.stringValue());
|
||||
return latLon[0] + "," + latLon[1];
|
||||
Point p = GeohashUtils.decode(f.stringValue(),ctx);
|
||||
return p.getY() + "," + p.getX();
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,11 +92,11 @@ public class GeoHashField extends FieldType implements SpatialQueryable {
|
|||
// latitude, longitude
|
||||
double[] latLon = new double[0];
|
||||
try {
|
||||
latLon = DistanceUtils.parseLatitudeLongitude(null, val);
|
||||
} catch (InvalidGeoException e) {
|
||||
latLon = ParseUtils.parseLatitudeLongitude(null, val);
|
||||
} catch (InvalidShapeException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
|
||||
}
|
||||
return GeoHashUtils.encode(latLon[0], latLon[1]);
|
||||
return GeohashUtils.encodeLatLon(latLon[0], latLon[1]);
|
||||
}
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue