LUCENE-1387 -- adding locallucene as new spatial contrib package.

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@730067 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2008-12-30 07:37:17 +00:00
parent eb64661216
commit 581c82ccbe
36 changed files with 3563 additions and 0 deletions

32
contrib/spatial/build.xml Normal file
View File

@ -0,0 +1,32 @@
<?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>
<property name="javac.source" value="1.5" />
<property name="javac.target" value="1.5" />
<import file="../contrib-build.xml"/>
</project>

View File

@ -0,0 +1,50 @@
<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-contrib</artifactId>
<version>@version@</version>
</parent>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-instantiated</artifactId>
<name>Lucene InstantiatedIndex</name>
<version>@version@</version>
<description>InstantiatedIndex, alternative RAM store for small corpora.</description>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,34 @@
/**
* 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 java.io.IOException;
import java.util.BitSet;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Filter;
/**
* Provide an optimized filter, by allowing the bitset from
* previous filters in the bitset to be used in the next part of the chain.
*
*/
public abstract class ISerialChainFilter extends Filter {
public abstract BitSet bits(IndexReader reader, BitSet bits) throws CorruptIndexException, IOException, Exception;
}

View File

@ -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;
/**
* TODO -- when solr moves NumberUtils to lucene, this should be redundant
*
* This is a copy of solr's number utils with only the functions we use...
*
* @deprecated will be replaced with lucene version of solr copy...
*/
@Deprecated
public class NumberUtils {
public static String long2sortableStr(long val) {
char[] arr = new char[5];
long2sortableStr(val,arr,0);
return new String(arr,0,5);
}
public static String double2sortableStr(double val) {
long f = Double.doubleToRawLongBits(val);
if (f<0) f ^= 0x7fffffffffffffffL;
return long2sortableStr(f);
}
public static double SortableStr2double(String val) {
long f = SortableStr2long(val,0,6);
if (f<0) f ^= 0x7fffffffffffffffL;
return Double.longBitsToDouble(f);
}
public static int long2sortableStr(long val, char[] out, int offset) {
val += Long.MIN_VALUE;
out[offset++] = (char)(val >>>60);
out[offset++] = (char)(val >>>45 & 0x7fff);
out[offset++] = (char)(val >>>30 & 0x7fff);
out[offset++] = (char)(val >>>15 & 0x7fff);
out[offset] = (char)(val & 0x7fff);
return 5;
}
public static long SortableStr2long(String sval, int offset, int len) {
long val = (long)(sval.charAt(offset++)) << 60;
val |= ((long)sval.charAt(offset++)) << 45;
val |= ((long)sval.charAt(offset++)) << 30;
val |= sval.charAt(offset++) << 15;
val |= sval.charAt(offset);
val -= Long.MIN_VALUE;
return val;
}
}

View File

@ -0,0 +1,199 @@
/**
* 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 java.io.IOException;
import java.util.BitSet;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Filter;
/**
*
* Provide a serial chain filter, passing the bitset in with the
* index reader to each of the filters in an ordered fashion.
*
* Based off chain filter, but will some improvements to allow a narrowed down
* filtering. Traditional filter required iteration through an IndexReader.
*
* By implementing the ISerialChainFilter class, you can create a bits(IndexReader reader, BitSet bits)
* @see org.apache.lucene.spatial.ISerialChainFilter
*
*/
public class SerialChainFilter extends Filter {
/**
* $Id: SerialChainFilter.java 136 2008-12-17 16:16:38Z ryantxu $
*/
private static final long serialVersionUID = 1L;
private Filter chain[];
public static final int SERIALAND = 1;
public static final int SERIALOR = 2;
public static final int AND = 3; // regular filters may be used first
public static final int OR = 4; // regular filters may be used first
public static final int DEFAULT = SERIALOR;
private int actionType[];
public SerialChainFilter(Filter chain[]){
this.chain = chain;
this.actionType = new int[] {DEFAULT};
}
public SerialChainFilter(Filter chain[], int actionType[]){
this.chain= chain;
this.actionType = actionType;
}
/* (non-Javadoc)
* @see org.apache.lucene.search.Filter#bits(org.apache.lucene.index.IndexReader)
*/
@Override
public BitSet bits(IndexReader reader) throws CorruptIndexException, IOException {
BitSet bits = new BitSet(reader.maxDoc());
int chainSize = chain.length;
int actionSize = actionType.length;
int i = 0;
/**
* taken from ChainedFilter, first and on an empty bitset results in 0
*/
if (actionType[i] == AND){
try {
bits = (BitSet) chain[i].bits(reader).clone();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
++i;
}
for( ; i < chainSize; i++) {
int action = (i < actionSize)? actionType[i]: DEFAULT;
switch (action){
case (SERIALAND):
try {
bits.and(((ISerialChainFilter) chain[i]).bits(reader, bits));
} catch (CorruptIndexException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case (SERIALOR):
try {
bits.or(((ISerialChainFilter) chain[i]).bits(reader,bits));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case (AND):
bits.and(chain[i].bits(reader));
break;
case (OR):
bits.and(chain[i].bits(reader));
break;
}
}
return bits;
}
/**
* @return the chain
*/
Filter[] getChain() {
return chain;
}
/**
* @return the actionType
*/
int[] getActionType() {
return actionType;
}
/**
* Returns true if <code>o</code> is equal to this.
*
* @see org.apache.lucene.search.RangeFilter#equals
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SerialChainFilter)) return false;
SerialChainFilter other = (SerialChainFilter) o;
if (this.chain.length != other.getChain().length ||
this.actionType.length != other.getActionType().length)
return false;
for (int i = 0; i < this.chain.length; i++) {
if (this.actionType[i] != other.getActionType()[i] ||
(!this.chain[i].equals(other.getChain()[i])))
return false;
}
return true;
}
/**
* Returns a hash code value for this object.
*
* @see org.apache.lucene.search.RangeFilter#hashCode
*/
@Override
public int hashCode() {
if (chain.length == 0)
return 0;
int h = chain[0].hashCode() ^ new Integer(actionType[0]).hashCode();
for (int i = 1; i < this.chain.length; i++) {
h ^= chain[i].hashCode();
h ^= new Integer(actionType[i]).hashCode();
}
return h;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("SerialChainFilter(");
for (int i = 0; i < chain.length; i++) {
switch(actionType[i]) {
case (SERIALAND): buf.append("SERIALAND"); break;
case (SERIALOR): buf.append("SERIALOR"); break;
case (AND): buf.append("AND"); break;
case (OR): buf.append("OR"); break;
default: buf.append(actionType[i]);
}
buf.append(" " + chain[i].toString() + " ");
}
return buf.toString().trim() + ")";
}
}

View File

@ -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.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.
*/
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
* @param i
* @param translation
* @return
*/
public CartesianPoint translate(int deltaX, int deltaY) {
return new CartesianPoint(this.x+deltaX, this.y+deltaY);
}
}

View File

@ -0,0 +1,23 @@
/**
* 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;
public enum DistanceUnits {
MILES,
KILOMETERS;
}

View File

@ -0,0 +1,135 @@
/**
* 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;
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;
}
@Override
public boolean equals(LatLng other) {
return lat==other.getFixedLat() && lng==other.getFixedLng();
}
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);
}
}

View File

@ -0,0 +1,116 @@
/**
* 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;
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 lattitude value " + lat);
this.lat=lat;
this.lng=lng;
}
public FloatLatLng(LatLng ll) {
this.lat=ll.getLat();
this.lng=ll.getLng();
}
@Override
public boolean equals(LatLng other) {
return lat==other.getLat() && lng==other.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);
}
}

View File

@ -0,0 +1,164 @@
/**
* 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 FixedLatLngTest
* @see FloatLatLng
*
*/
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();
public abstract boolean equals(LatLng other);
@Override
public boolean equals(Object other) {
if (!(other instanceof LatLng)) return false;
return equals((LatLng)other);
}
/**
* 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).
*
* @return
*/
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
* @return
*/
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 distace, 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 diffeernce. 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
* @return
*/
public abstract LatLng calculateMidpoint(LatLng other);
}

View File

@ -0,0 +1,118 @@
/**
* 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;
/**
* Imported from mq java client. No changes made.
*/
public class DistanceApproximation
{
private double m_testLat;
private double m_testLng;
private double m_mpd;
private static final double m_milesPerLngDeg[]={
69.170976f, 69.160441f, 69.128838f, 69.076177f, 69.002475f,
68.907753f, 68.792041f, 68.655373f, 68.497792f, 68.319345f,
68.120088f, 67.900079f, 67.659387f, 67.398085f, 67.116253f,
66.813976f, 66.491346f, 66.148462f, 65.785428f, 65.402355f,
64.999359f, 64.576564f, 64.134098f, 63.672096f, 63.190698f,
62.690052f, 62.170310f, 61.631630f, 61.074176f, 60.498118f,
59.903632f, 59.290899f, 58.660106f, 58.011443f, 57.345111f,
56.661310f, 55.960250f, 55.242144f, 54.507211f, 53.755675f,
52.987764f, 52.203713f, 51.403761f, 50.588151f, 49.757131f,
48.910956f, 48.049882f, 47.174172f, 46.284093f, 45.379915f,
44.461915f, 43.530372f, 42.585570f, 41.627796f, 40.657342f,
39.674504f, 38.679582f, 37.672877f, 36.654698f, 35.625354f,
34.585159f, 33.534429f, 32.473485f, 31.402650f, 30.322249f,
29.232613f, 28.134073f, 27.026963f, 25.911621f, 24.788387f,
23.657602f, 22.519612f, 21.374762f, 20.223401f, 19.065881f,
17.902554f, 16.733774f, 15.559897f, 14.381280f, 13.198283f,
12.011266f, 10.820591f, 9.626619f, 8.429716f, 7.230245f,
6.028572f, 4.825062f, 3.620083f, 2.414002f, 1.207185f,
1.000000f};
public static final double MILES_PER_LATITUDE = 69.170976f;
public static final double KILOMETERS_PER_MILE = 1.609347f;
public DistanceApproximation()
{
}
public void setTestPoint(double lat, double lng)
{
m_testLat = lat;
m_testLng = lng;
m_mpd = m_milesPerLngDeg[(int)(Math.abs(lat) + 0.5f)];
}
// Approximate arc distance between 2 lat,lng positions using miles per
// latitude and longitude degree
public double getDistanceSq(double lat, double lng)
{
double latMiles = (lat - m_testLat) * MILES_PER_LATITUDE;
// Approximate longitude miles using the miles per degree assuming the
// middle latitude/longitude. This is less accurate at high (near
// polar) latitudes but no road network is present at the poles!
// If we ever have any roads crossing the international date we will
// have to worry about that case.
double lngMiles = (lng - m_testLng) * m_mpd;
// Find the squared distance by the Pythagorean theorem (without sqrt)
return (latMiles * latMiles + lngMiles * lngMiles);
}
// Approximate arc distance between a segment (with lat,lng endpoints) and
// the test position
public double getDistanceSq(double lat1, double lng1, double lat2, double lng2)
{
// Check if lat1,lng1 is closest point. Construct a vector from point1
// to point2 (v1) and another from point 1 to the test point (v2).
// If dot product is negative then point 1 is the closest point
double v1y = lat2 - lat1;
double v1x = lng2 - lng1;
double v2y = m_testLat - lat1;
double v2x = m_testLng - lng1;
double dot = v1x * v2x + v1y * v2y;
if (dot <= 0.0f)
return getDistanceSq(lat1, lng1);
// Get the component of vector v2 along v1. If component is greater
// than 1 then the endpoint is the closest point.
double c = dot / (v1x * v1x + v1y * v1y);
if (c >= 1.0f)
return getDistanceSq(lat2, lng2);
// Since we are working io lat,lng space we need to find the point
// along p1->p2 such that q->pt is perpendicular to p1->p2. We
// then find the distance squared between Q and pt.
return getDistanceSq((lat1 + v1y * c), (lng1 + v1x * c));
}
// Return the number of miles per degree of longitude
public static double getMilesPerLngDeg(double lat)
{
return (Math.abs(lat) <= 90.0) ? m_milesPerLngDeg[(int)(Math.abs(lat) + 0.5f)] : 69.170976f;
}
public static double getMilesPerLatDeg() {
return MILES_PER_LATITUDE;
}
}

View File

@ -0,0 +1,233 @@
/**
* 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.
*/
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.
*
* @param
* @param
*/
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 paramterizing 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();
}
}

View File

@ -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.geometry.shape;
/**
* Common set of operations available on 2d shapes.
*/
public interface Geometry2D {
/**
* Translate according to the vector
* @param v
*/
public void translate(Vector2D v);
/**
* Does the shape contain the given point
* @param p
* @return
*/
public boolean contains(Point2D p);
/**
* Return the area
* @return
*/
public double area();
/**
* Return the centroid
* @return
*/
public Point2D centroid();
/**
* Returns information about how this shape intersects the given rectangle
* @param r
* @return
*/
public IntersectCase intersect(Rectangle r);
}

View File

@ -0,0 +1,25 @@
/**
* 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;
public enum IntersectCase {
WITHIN,
CONTAINS,
OUTSIDE,
INTERSECTS;
}

View File

@ -0,0 +1,105 @@
/**
* 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.
*/
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.
* @return
*/
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 boolean equals(Object otherObj) {
if (!(otherObj instanceof LLRect)) return false;
return equals((LLRect)otherObj);
}
public boolean equals(LLRect other) {
return getLowerLeft().equals(other.getLowerLeft()) && getUpperRight().equals(other.getUpperRight());
}
@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
* @return
*/
public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
double miplatdeg=DistanceApproximation.getMilesPerLngDeg(center.getLat());
double miplngdeg=DistanceApproximation.getMilesPerLatDeg();
double lngDelta=(widthMi/2)/miplngdeg;
double latDelta=(heightMi/2)/miplatdeg;
// TODO: Prob only works in northern hemisphere?
LatLng ll=new FloatLatLng(center.getLat()-latDelta, center.getLng()-lngDelta);
LatLng ur=new FloatLatLng(center.getLat()+latDelta, center.getLng()+lngDelta);
return new LLRect(ll, ur);
}
/**
* Returns a rectangle shape for the bounding box
* @return
*/
public Rectangle toRectangle() {
return new Rectangle(ll.getLng(), ll.getLat(), ur.getLng(), ur.getLat());
}
}

View File

@ -0,0 +1,81 @@
/**
* 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.
*/
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 segement 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();
}
}

View File

@ -0,0 +1,116 @@
/**
* 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.
*/
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 boolean equals(Point2D other) {
return other!=null && x==other.x && y==other.y;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Point2D)) return false;
return equals((Point2D)other);
}
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();
}
}

View File

@ -0,0 +1,103 @@
/**
* 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.
*/
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 boolean equals(Rectangle other) {
return other.ptMin.equals(ptMin) && other.ptMax.equals(ptMax);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Rectangle)) return false;
return equals((Rectangle)other);
}
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;
}
}

View File

@ -0,0 +1,120 @@
/**
* 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
*/
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;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Vector2D))
return false;
return equals((Vector2D) other);
}
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);
}
}

View File

@ -0,0 +1,210 @@
/**
* 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.BitSet;
import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.index.TermEnum;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.NumberUtils;
/**
* An implementation of org.apache.lucene.search.RangeFilter that
* caches values extracted from the index.
*
*/
public class BoundaryBoxFilter extends Filter {
private static final long serialVersionUID = 1L;
private String fieldName;
private String lowerTerm;
private String upperTerm;
private boolean includeLower;
private boolean includeUpper;
private Logger log = Logger.getLogger(getClass().getName());
// cache of values extracted from the index
// TODO: add generics
//private Map coords;
/**
* @param fieldName The field this range applies to
* @param lowerTerm The lower bound on this range
* @param upperTerm The upper bound on this range
* @param includeLower Does this range include the lower bound?
* @param includeUpper Does this range include the upper bound?
* @throws IllegalArgumentException if both terms are null or if
* lowerTerm is null and includeLower is true (similar for upperTerm
* and includeUpper)
*/
public BoundaryBoxFilter(String fieldName, String lowerTerm, String upperTerm,
boolean includeLower, boolean includeUpper) {
this.fieldName = fieldName;
this.lowerTerm = lowerTerm;
this.upperTerm = upperTerm;
this.includeLower = includeLower;
this.includeUpper = includeUpper;
if (null == lowerTerm && null == upperTerm) {
throw new IllegalArgumentException
("At least one value must be non-null");
}
if (includeLower && null == lowerTerm) {
throw new IllegalArgumentException
("The lower bound must be non-null to be inclusive");
}
if (includeUpper && null == upperTerm) {
throw new IllegalArgumentException
("The upper bound must be non-null to be inclusive");
}
}
/**
* Returns a BitSet with true for documents which should be
* permitted in search results, and false for those that should
* not.
*/
@Override
public BitSet bits(IndexReader reader) throws IOException {
long start = System.currentTimeMillis();
BitSet bits = new BitSet(reader.maxDoc());
TermEnum enumerator =
(null != lowerTerm
? reader.terms(new Term(fieldName, lowerTerm))
: reader.terms(new Term(fieldName,"")));
//coords = new HashMap(enumerator.docFreq());
try {
if (enumerator.term() == null) {
return bits;
}
boolean checkLower = false;
if (!includeLower) // make adjustments to set to exclusive
checkLower = true;
TermDocs termDocs = reader.termDocs();
try {
do {
Term term = enumerator.term();
if (term != null && term.field().equals(fieldName)) {
if (!checkLower || null==lowerTerm || term.text().compareTo(lowerTerm) > 0) {
checkLower = false;
if (upperTerm != null) {
int compare = upperTerm.compareTo(term.text());
// if beyond the upper term, or is exclusive and
// this is equal to the upper term, break out
if ((compare < 0) ||
(!includeUpper && compare==0)) {
break;
}
}
// we have a good term, find the docs
termDocs.seek(enumerator.term());
while (termDocs.next()) {
bits.set(termDocs.doc());
}
}
}
else {
break;
}
}
while (enumerator.next());
}
finally {
termDocs.close();
}
}
finally {
enumerator.close();
}
long end = System.currentTimeMillis();
log.info("BoundaryBox Time Taken: "+ (end - start));
return bits;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(fieldName);
buffer.append(":");
buffer.append(includeLower ? "[" : "{");
if (null != lowerTerm) {
buffer.append(NumberUtils.SortableStr2double(lowerTerm));
}
buffer.append("-");
if (null != upperTerm) {
buffer.append(NumberUtils.SortableStr2double(upperTerm));
}
buffer.append(includeUpper ? "]" : "}");
return buffer.toString();
}
public String getFieldName() {
return fieldName;
}
/**
* Returns true if <code>o</code> is equal to this.
*
* @see org.apache.lucene.search.RangeFilter#equals
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BoundaryBoxFilter)) return false;
BoundaryBoxFilter other = (BoundaryBoxFilter) o;
if (!this.fieldName.equals(other.fieldName)
|| this.includeLower != other.includeLower
|| this.includeUpper != other.includeUpper
) { return false; }
if (this.lowerTerm != null ? !this.lowerTerm.equals(other.lowerTerm) : other.lowerTerm != null) return false;
if (this.upperTerm != null ? !this.upperTerm.equals(other.upperTerm) : other.upperTerm != null) return false;
return true;
}
/**
* Returns a hash code value for this object.
*
* @see org.apache.lucene.search.RangeFilter#hashCode
*/
@Override
public int hashCode() {
int h = fieldName.hashCode();
h ^= lowerTerm != null ? lowerTerm.hashCode() : 0xB6ECE882;
h = (h << 1) | (h >>> 31); // rotate to distinguish lower from upper
h ^= (upperTerm != null ? (upperTerm.hashCode()) : 0x91BEC2C2);
h ^= (includeLower ? 0xD484B933 : 0)
^ (includeUpper ? 0x6AE423AC : 0);
return h;
}
}

View File

@ -0,0 +1,106 @@
/**
* 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.math.BigDecimal;
import java.math.RoundingMode;
import java.util.logging.Logger;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.geometry.shape.Rectangle;
import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
import org.apache.lucene.spatial.tier.projections.IProjector;
import org.apache.lucene.spatial.tier.projections.SinusoidalProjector;
/**
*
*/
public class CartesianPolyFilter {
private IProjector projector = new SinusoidalProjector();
private Logger log = Logger.getLogger(getClass().getName());
public Shape getBoxShape(double latitude, double longitude, int miles)
{
Rectangle box = DistanceUtils.getInstance().getBoundary(latitude, longitude, miles);
double latY = box.getMaxPoint().getY();//box.getY();
double latX = box.getMinPoint().getY() ; //box.getMaxY();
double longY = box.getMaxPoint().getX(); ///box.getX();
double longX = box.getMinPoint().getX();//box.getMaxX();
CartesianTierPlotter ctp = new CartesianTierPlotter(2, projector);
int bestFit = ctp.bestFit(miles);
log.info("Best Fit is : " + bestFit);
ctp = new CartesianTierPlotter(bestFit, projector);
Shape shape = new Shape(ctp.getTierFieldName());
// generate shape
// iterate from startX->endX
// iterate from startY -> endY
// shape.add(currentLat.currentLong);
double beginAt = ctp.getTierBoxId(latX, longX);
double endAt = ctp.getTierBoxId(latY, longY);
double tierVert = ctp.getTierVerticalPosDivider();
log.fine(" | "+ 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();
log.fine("scale "+scale+" startX "+ startX + " endX "+endX +" startY "+ startY + " endY "+ endY +" tierVert "+ tierVert);
double xInc = 1.0d / tierVert;
xInc = new BigDecimal(xInc).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
for (; startX <= endX; startX++){
double itY = startY;
while (itY <= endY){
//create a boxId
// startX.startY
double boxId = startX + itY ;
shape.addBox(boxId);
//System.out.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, int miles)
{
Shape shape = getBoxShape(latitude, longitude, miles);
return new CartesianShapeFilter(shape, shape.getTierId());
}
}

View File

@ -0,0 +1,75 @@
/**
* 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.BitSet;
import java.util.List;
import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.NumberUtils;
public class CartesianShapeFilter extends Filter {
/**
*
*/
private static final long serialVersionUID = 1L;
private Shape shape;
private Logger log = Logger.getLogger(getClass().getName());
private String fieldName;
CartesianShapeFilter(Shape shape, String fieldName){
this.shape = shape;
this.fieldName = fieldName;
}
@Override
public BitSet bits(IndexReader reader) throws IOException {
long start = System.currentTimeMillis();
BitSet bits = new BitSet(reader.maxDoc());
TermDocs termDocs = reader.termDocs();
List<Double> area = shape.getArea();
int sz = area.size();
log.info("Area size "+ sz);
// iterate through each boxid
for (int i =0; i< sz; i++) {
double boxId = area.get(i).doubleValue();
termDocs.seek(new Term(fieldName,
NumberUtils.double2sortableStr(boxId)));
// iterate through all documents
// which have this boxId
while (termDocs.next()) {
bits.set(termDocs.doc());
}
}
long end = System.currentTimeMillis();
log.info("BoundaryBox Time Taken: "+ (end - start) + " found: "+bits.cardinality()+" candidates");
return bits;
}
}

View File

@ -0,0 +1,248 @@
/**
* 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.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.spatial.ISerialChainFilter;
import org.apache.lucene.spatial.tier.DistanceHandler.Precision;
import org.apache.lucene.spatial.NumberUtils;
public class DistanceFilter extends ISerialChainFilter {
/**
*
*/
private static final long serialVersionUID = 1L;
private double distance;
private double lat;
private double lng;
private String latField;
private String lngField;
private Logger log = Logger.getLogger(getClass().getName());
private Map<Integer,Double> distances = null;
private Precision precise = null;
/**
* Provide a distance filter based from a center point with a radius
* in miles
* @param lat
* @param lng
* @param miles
* @param latField
* @param lngField
*/
public DistanceFilter(double lat, double lng, double miles, String latField, String lngField){
distance = miles;
this.lat = lat;
this.lng = lng;
this.latField = latField;
this.lngField = lngField;
}
public Map<Integer,Double> getDistances(){
return distances;
}
public Double getDistance(int docid){
return distances.get(docid);
}
@Override
public BitSet bits(IndexReader reader) throws IOException {
/* Create a BitSet to store the result */
int maxdocs = reader.numDocs();
BitSet bits = new BitSet(maxdocs);
setPrecision(maxdocs);
// create an intermediate cache to avoid recomputing
// distances for the same point
// TODO: Why is this a WeakHashMap?
WeakHashMap<String,Double> cdistance = new WeakHashMap<String,Double>(maxdocs);
String[] latIndex = FieldCache.DEFAULT.getStrings(reader, latField);
String[] lngIndex = FieldCache.DEFAULT.getStrings(reader, lngField);
/* store calculated distances for reuse by other components */
distances = new HashMap<Integer,Double>(maxdocs);
for (int i = 0 ; i < maxdocs; i++) {
String sx = latIndex[i];
String sy = lngIndex[i];
double x = NumberUtils.SortableStr2double(sx);
double y = NumberUtils.SortableStr2double(sy);
// round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise);
String ck = new Double(x).toString()+","+new Double(y).toString();
Double cachedDistance = cdistance.get(ck);
double d;
if(cachedDistance != null){
d = cachedDistance.doubleValue();
} else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
cdistance.put(ck, d);
}
distances.put(i, d);
if (d < distance){
bits.set(i);
}
}
return bits;
}
@Override
public BitSet bits(IndexReader reader, BitSet bits) throws Exception {
/* Create a BitSet to store the result */
int size = bits.cardinality();
BitSet result = new BitSet(size);
/* create an intermediate cache to avoid recomputing
distances for the same point */
HashMap<String,Double> cdistance = new HashMap<String,Double>(size);
/* store calculated distances for reuse by other components */
distances = new HashMap<Integer,Double>(size);
long start = System.currentTimeMillis();
String[] latIndex = FieldCache.DEFAULT.getStrings(reader, latField);
String[] lngIndex = FieldCache.DEFAULT.getStrings(reader, lngField);
/* loop over all set bits (hits from the boundary box filters) */
int i = bits.nextSetBit(0);
while (i >= 0){
double x,y;
// if we have a completed
// filter chain, lat / lngs can be retrived from
// memory rather than document base.
String sx = latIndex[i];
String sy = lngIndex[i];
x = NumberUtils.SortableStr2double(sx);
y = NumberUtils.SortableStr2double(sy);
// round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise);
String ck = new Double(x).toString()+","+new Double(y).toString();
Double cachedDistance = cdistance.get(ck);
double d;
if(cachedDistance != null){
d = cachedDistance.doubleValue();
} else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
//d = DistanceUtils.getLLMDistance(lat, lng, x, y);
cdistance.put(ck, d);
}
distances.put(i, d);
if (d < distance){
result.set(i);
}
i = bits.nextSetBit(i+1);
}
long end = System.currentTimeMillis();
log.fine("Time taken : "+ (end - start) +
", results : "+ distances.size() +
", cached : "+ cdistance.size() +
", incoming size: "+ size);
cdistance = null;
return result;
}
/** Returns true if <code>o</code> is equal to this. */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DistanceFilter)) return false;
DistanceFilter other = (DistanceFilter) o;
if (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 = new Double(distance).hashCode();
h ^= new Double(lat).hashCode();
h ^= new Double(lng).hashCode();
h ^= latField.hashCode();
h ^= lngField.hashCode();
return h;
}
private void setPrecision(int maxDocs) {
precise = Precision.EXACT;
if (maxDocs > 1000 && distance > 10) {
precise = Precision.TWENTYFEET;
}
if (maxDocs > 10000 && distance > 10){
precise = Precision.TWOHUNDREDFEET;
}
}
public void setDistances(Map<Integer, Double> distances) {
this.distances = distances;
}
}

View File

@ -0,0 +1,100 @@
/**
* 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.HashMap;
import java.util.Map;
/**
* Provide a high level access point to distances
* Used by DistanceSortSource and DistanceQuery
*
* @author pjaol
*
*/
public class DistanceHandler {
private Map<Integer,Double> distances;
public enum Precision {EXACT, TWOFEET, TWENTYFEET, TWOHUNDREDFEET};
private Precision precise;
public DistanceHandler (Map<Integer,Double> distances, Precision precise){
this.distances = distances;
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.getInstance().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 = new Double(xLat).toString() +","+ new Double(xLng).toString();
Double d = (distances.get(k));
if (d != null){
return d.doubleValue();
}
}
//all else fails calculate the distances
return DistanceUtils.getInstance().getDistanceMi(centerLat, centerLng, lat, lng);
}
public static void main(String args[]){
DistanceHandler db = new DistanceHandler(new HashMap(), Precision.TWOHUNDREDFEET);
System.out.println(DistanceHandler.getPrecision(-1234.123456789, db.getPrecision()));
}
}

View File

@ -0,0 +1,104 @@
/**
* 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.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SerialChainFilter;
public class DistanceQuery {
private static final long serialVersionUID = 1L;
public BoundaryBoxFilter latFilter;
public BoundaryBoxFilter lngFilter;
public DistanceFilter distanceFilter;
private double lat;
private double lng;
private double miles;
private Filter cartesianFilter;
/**
* Create a distance query using
* a boundary box wrapper around a more precise
* DistanceFilter.
*
* @see SerialChainFilter
* @param lat
* @param lng
* @param miles
*/
public DistanceQuery (double lat, double lng, double miles, String latField, String lngField, boolean needPrecise){
this.lat = lat;
this.lng = lng;
this.miles = miles;
CartesianPolyFilter cpf = new CartesianPolyFilter();
cartesianFilter = cpf.getBoundingArea(lat, lng, (int)miles);
/* create precise distance filter */
if( needPrecise)
distanceFilter = new DistanceFilter(lat, lng, miles, latField, lngField);
}
/**
* Create a distance query using
* a boundary box wrapper around a more precise
* DistanceFilter.
*
* @see SerialChainFilter
* @param lat
* @param lng
* @param miles
*/
public Filter getFilter() {
return new SerialChainFilter(new Filter[] {cartesianFilter, distanceFilter},
new int[] {SerialChainFilter.AND,
SerialChainFilter.SERIALAND});
}
public Filter getFilter(Query query) {
QueryWrapperFilter qf = new QueryWrapperFilter(query);
return new SerialChainFilter(new Filter[] {cartesianFilter, qf, distanceFilter},
new int[] {SerialChainFilter.AND,
SerialChainFilter.AND,
SerialChainFilter.SERIALAND});
}
public Query getQuery() {
return new ConstantScoreQuery(getFilter());
}
@Override
public String toString() {
return "DistanceQuery lat: " + lat + " lng: " + lng + " miles: "+ miles;
}
}

View File

@ -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.tier;
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.ScoreDocComparator;
import org.apache.lucene.search.SortComparatorSource;
import org.apache.lucene.search.SortField;
public class DistanceSortSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
private DistanceFilter distanceFilter;
private DistanceScoreDocLookupComparator dsdlc;
public DistanceSortSource (Filter distanceFilter){
this.distanceFilter = (DistanceFilter)distanceFilter;
}
public void cleanUp() {
distanceFilter = null;
if (dsdlc !=null)
dsdlc.cleanUp();
dsdlc = null;
}
public ScoreDocComparator newComparator(IndexReader reader, String field) throws IOException {
dsdlc = new DistanceScoreDocLookupComparator(reader, distanceFilter);
return dsdlc;
}
private class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private DistanceFilter distanceFilter;
public DistanceScoreDocLookupComparator(IndexReader reader, DistanceFilter distanceFilter) {
this.distanceFilter = distanceFilter;
return;
}
public int compare(ScoreDoc aDoc, ScoreDoc bDoc) {
// if (this.distances == null) {
// distances = distanceFilter.getDistances();
// }
double a = distanceFilter.getDistance(aDoc.doc);
double b = distanceFilter.getDistance(bDoc.doc);
if (a > b) return 1;
if (a < b )return -1;
return 0;
}
public int sortType() {
return SortField.DOUBLE;
}
public Comparable sortValue(ScoreDoc iDoc) {
return distanceFilter.getDistance(iDoc.doc);
}
public void cleanUp() {
distanceFilter = null;
}
}
}

View File

@ -0,0 +1,63 @@
/**
* 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.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;
public class DistanceUtils {
static DistanceUtils instance = new DistanceUtils();
public static DistanceUtils getInstance()
{
return instance;
}
public 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 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 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 );
}
}

View File

@ -0,0 +1,30 @@
/**
* 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;
public class InvalidGeoException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public InvalidGeoException(String message){
super(message);
}
}

View File

@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial.tier;
import java.util.ArrayList;
import java.util.List;
/**
* @author pjaol
*
*/
public class Shape {
private List<Double> area = new ArrayList<Double>();
private String tierId;
public Shape (String tierId){
this.tierId = tierId;
}
public void addBox(double boxId){
area.add(boxId);
}
public List<Double> getArea(){
return area;
}
public String getTierId(){
return tierId;
}
public boolean isInside(double boxId){
return area.contains(boxId);
}
}

View File

@ -0,0 +1,170 @@
/**
* 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;
/**
*
*/
public class CartesianTierPlotter {
int tierLevel;
int tierLength;
int tierBoxes;
int tierVerticalPosDivider;
IProjector projector;
final String fieldPrefix = "_localTier";
Double idd = new Double(180);
public CartesianTierPlotter (int tierLevel, IProjector projector) {
this.tierLevel = tierLevel;
this.projector = projector;
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 = new Double(Math.ceil(
Math.log10(new Integer(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 transposded in to position
* coordinates.
*
* @param latitude
* @param longitude
* @return
*/
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&lt;tiedId&gt;
* @return
*/
public String getTierFieldName (){
return fieldPrefix + this.tierLevel;
}
/**
* get the string name representing tierId
* _localTier&lt;tierId&gt;
* @param tierId
* @return
*/
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
*
* @param latitude
* @param longitude
* @return
*/
public int bestFit(int 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);
System.out.println("corner "+ corner);
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
* @return
*/
public double log2(double value) {
return Math.log(value) / Math.log(2);
}
}

View File

@ -0,0 +1,23 @@
/**
* 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;
public interface IProjector {
public String coordsAsString(double latitude, double longitude);
public double[] coords(double latitude, double longitude);
}

View File

@ -0,0 +1,41 @@
/**
* 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 cartisian map
*
*/
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;
}
}

View File

@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial.tier;
import java.text.DecimalFormat;
import org.apache.lucene.spatial.tier.DistanceUtils;
public class DistanceCheck {
/**
* @param args
*/
public static void main(String[] args) {
double lat1 = 0;
double long1 = 0;
double lat2 = 0;
double long2 = 0;
for (int i =0; i < 90; i++){
double dis = DistanceUtils.getInstance().getDistanceMi(lat1, long1, lat2, long2);
lat1 +=1;
lat2 = lat1 + 0.001;
System.out.println(lat1+","+long1+","+lat2+","+long2+","+formatDistance(dis));
}
}
public static String formatDistance (Double d){
DecimalFormat df1 = new DecimalFormat("####.000000");
return df1.format(d);
}
}

View File

@ -0,0 +1 @@
/** * 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; /** * */ public class PolyShape { private static double lat = 38.969398; private static double lng= -77.386398; private static int miles = 1000; /** * @param args */ public static void main(String[] args) { CartesianPolyFilter cpf = new CartesianPolyFilter(); cpf.getBoxShape(lat, lng, miles); } }

View File

@ -0,0 +1,225 @@
/**
* 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.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.spatial.tier.DistanceQuery;
import org.apache.lucene.spatial.tier.DistanceSortSource;
import org.apache.lucene.spatial.tier.DistanceUtils;
import org.apache.lucene.spatial.tier.InvalidGeoException;
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.store.RAMDirectory;
import org.apache.lucene.spatial.NumberUtils;
import org.apache.lucene.search.function.CustomScoreQuery;
import org.apache.lucene.search.function.FieldScoreQuery;
import org.apache.lucene.search.function.FieldScoreQuery.Type;
/**
*
*/
public class TestCartesian extends TestCase{
/**
* @param args
*/
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 IProjector project = new SinusoidalProjector();
@Override
protected void setUp() throws IOException {
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true);
setUpPlotter( 2, 15);
addData(writer);
}
private void setUpPlotter(int base, int top) {
for (; base <= top; base ++){
ctps.add(new CartesianTierPlotter(base,project ));
}
}
private void addPoint(IndexWriter writer, String name, double lat, double lng) throws IOException{
Document doc = new Document();
doc.add(new Field("name", name,Field.Store.YES, Field.Index.TOKENIZED));
// convert the lat / long to lucene fields
doc.add(new Field(latField, NumberUtils.double2sortableStr(lat),Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field(lngField, NumberUtils.double2sortableStr(lng),Field.Store.YES, Field.Index.UN_TOKENIZED));
// add a default meta field to make searching all documents easy
doc.add(new Field("metafile", "doc",Field.Store.YES, Field.Index.TOKENIZED));
int ctpsize = ctps.size();
for (int i =0; i < ctpsize; i++){
CartesianTierPlotter ctp = ctps.get(i);
doc.add(new Field(ctp.getTierFieldName(),
NumberUtils.double2sortableStr(ctp.getTierBoxId(lat,lng)),
Field.Store.YES,
Field.Index.NO_NORMS));
}
writer.addDocument(doc);
}
private void addData(IndexWriter writer) throws IOException {
addPoint(writer,"McCormick &amp; 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 &amp; 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.flush();
writer.commit();
//writer.close();
}
public void testRange() throws IOException, InvalidGeoException {
searcher = new IndexSearcher(directory);
final double miles = 6.0;
// create a distance query
final DistanceQuery dq = new DistanceQuery(lat, lng, miles, latField, lngField, true);
System.out.println(dq);
//create a term query to search against all documents
Query tq = new TermQuery(new Term("metafile", "doc"));
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
CustomScoreQuery customScore = new CustomScoreQuery(tq,fsQuery){
@Override
public float customScore(int doc, float subQueryScore, float valSrcScore){
//System.out.println(doc);
if (dq.distanceFilter.getDistance(doc) == null)
return 0;
double distance = dq.distanceFilter.getDistance(doc);
// boost score shouldn't exceed 1
if (distance < 1.0d)
distance = 1.0d;
//boost by distance is invertly proportional to
// to distance from center point to location
float score = new Float((miles - distance) / miles ).floatValue();
return score * subQueryScore;
}
};
// Create a distance sort
// As the radius filter has performed the distance calculations
// already, pass in the filter to reuse the results.
//
DistanceSortSource dsort = new DistanceSortSource(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
Hits hits = searcher.search(customScore, dq.getFilter()); //,sort);
int results = hits.length();
// 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.
System.out.println("Distance Filter filtered: " + distances.size());
System.out.println("Results: " + results);
System.out.println("=============================");
System.out.println("Distances should be 14 "+ distances.size());
System.out.println("Results should be 7 "+ results);
assertEquals(14, distances.size());
assertEquals(7, results);
for(int i =0 ; i < results; i++){
Document d = hits.doc(i);
String name = d.get("name");
double rsLat = NumberUtils.SortableStr2double(d.get(latField));
double rsLng = NumberUtils.SortableStr2double(d.get(lngField));
Double geo_distance = distances.get(hits.id(i));
double distance = DistanceUtils.getInstance().getDistanceMi(lat, lng, rsLat, rsLng);
double llm = DistanceUtils.getInstance().getLLMDistance(lat, lng, rsLat, rsLng);
System.out.println("Name: "+ name +", Distance (res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ hits.score(i));
assertTrue(Math.abs((distance - llm)) < 1);
assertTrue((distance < miles ));
}
}
}

View File

@ -0,0 +1,137 @@
/**
* 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.Map;
import junit.framework.TestCase;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.spatial.tier.DistanceQuery;
import org.apache.lucene.spatial.tier.DistanceSortSource;
import org.apache.lucene.spatial.tier.DistanceUtils;
import org.apache.lucene.spatial.tier.InvalidGeoException;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.spatial.NumberUtils;
/**
*
*/
public class TestDistance extends TestCase{
private RAMDirectory directory;
private IndexSearcher searcher;
// reston va
private double lat = 38.969398;
private double lng= -77.386398;
private String latField = "lat";
private String lngField = "lng";
@Override
protected void setUp() throws IOException {
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true);
addData(writer);
}
private void addPoint(IndexWriter writer, String name, double lat, double lng) throws IOException{
Document doc = new Document();
doc.add(new Field("name", name,Field.Store.YES, Field.Index.TOKENIZED));
// convert the lat / long to lucene fields
doc.add(new Field(latField, NumberUtils.double2sortableStr(lat),Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field(lngField, NumberUtils.double2sortableStr(lng),Field.Store.YES, Field.Index.UN_TOKENIZED));
// add a default meta field to make searching all documents easy
doc.add(new Field("metafile", "doc",Field.Store.YES, Field.Index.TOKENIZED));
writer.addDocument(doc);
}
private void addData(IndexWriter writer) throws IOException {
addPoint(writer,"McCormick &amp; 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 &amp; 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.flush();
}
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());
// }
}