diff --git a/contrib/CHANGES.txt b/contrib/CHANGES.txt index 735e8751928..e97657bd7ca 100644 --- a/contrib/CHANGES.txt +++ b/contrib/CHANGES.txt @@ -32,7 +32,11 @@ API Changes * LUCENE-2108: Add SpellChecker.close, to close the underlying reader. (Eirik Bjørsnøs via Mike McCandless) - + + * LUCENE-2147: Spatial GeoHashUtils now always decode GeoHash strings + with full precision. GeoHash#decode_exactly(String) was merged into + GeoHash#decode(String). (Chris Male, Simon Willnauer) + * LUCENE-2165: Add a constructor to SnowballAnalyzer that takes a Set of stopwords, and deprecate the String[] one. (Nick Burch via Robert Muir) diff --git a/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashUtils.java b/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashUtils.java index 2462a47ccfc..be7c1433d54 100644 --- a/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashUtils.java +++ b/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashUtils.java @@ -21,154 +21,119 @@ import java.util.HashMap; import java.util.Map; /** - * Based on http://en.wikipedia.org/wiki/Geohash - * - *
NOTE: This API is still in
- * flux and might change in incompatible ways in the next
- * release.
+ * Utilities for encoding and decoding geohashes. Based on
+ * http://en.wikipedia.org/wiki/Geohash.
*/
public class GeoHashUtils {
- // geohash's char map
- // no a's i's l's o's
- // old MacDonal wouldn't be happy
- private static char[] _base32 = {'0','1','2','3','4','5','6','7','8','9',
- 'b','c','d','e','f','g','h','j','k','m',
- 'n','p','q','r','s','t','u','v','w','x',
- 'y','z'} ;
-
- private final static Map NOTE: This API is still in
- * flux and might change in incompatible ways in the next
- * release.
+ * Enum representing difference distance units, currently only kilometers and
+ * miles
*/
public enum DistanceUnits {
- MILES,
- KILOMETERS;
+
+ MILES("miles", 3959, 24902),
+ KILOMETERS("km", 6371, 40076);
+
+ private static final double MILES_KILOMETRES_RATIO = 1.609344;
+
+ private final String unit;
+
+ private final double earthCircumference;
+
+ private final double earthRadius;
+
+ /**
+ * Creates a new DistanceUnit that represents the given unit
+ *
+ * @param unit Distance unit in String form
+ * @param earthRadius Radius of the Earth in the specific distance unit
+ * @param earthCircumfence Circumference of the Earth in the specific distance unit
+ */
+ DistanceUnits(String unit, double earthRadius, double earthCircumfence) {
+ this.unit = unit;
+ this.earthCircumference = earthCircumfence;
+ this.earthRadius = earthRadius;
+ }
+
+ /**
+ * Returns the DistanceUnit which represents the given unit
+ *
+ * @param unit Unit whose DistanceUnit should be found
+ * @return DistanceUnit representing the unit
+ * @throws IllegalArgumentException if no DistanceUnit which represents the given unit is found
+ */
+ public static DistanceUnits findDistanceUnit(String unit) {
+ if (MILES.getUnit().equals(unit)) {
+ return MILES;
+ }
+
+ if (KILOMETERS.getUnit().equals(unit)) {
+ return KILOMETERS;
+ }
+
+ throw new IllegalArgumentException("Unknown distance unit " + unit);
+ }
+
+ /**
+ * Converts the given distance in given DistanceUnit, to a distance in the unit represented by {@code this}
+ *
+ * @param distance Distance to convert
+ * @param from Unit to convert the distance from
+ * @return Given distance converted to the distance in the given unit
+ */
+ public double convert(double distance, DistanceUnits from) {
+ if (from == this) {
+ return distance;
+ }
+ return (this == MILES) ? distance / MILES_KILOMETRES_RATIO : distance * MILES_KILOMETRES_RATIO;
+ }
+
+ /**
+ * Returns the string representation of the distance unit
+ *
+ * @return String representation of the distance unit
+ */
+ public String getUnit() {
+ return unit;
+ }
+
+ /**
+ * Returns the average earth radius
+ *
+ * @return the average earth radius
+ */
+ public double earthRadius() {
+ return earthRadius;
+ }
+
+ /**
+ * Returns the circumference of the Earth
+ *
+ * @return the circumference of the Earth
+ */
+ public double earthCircumference() {
+ return earthCircumference;
+ }
}
+
diff --git a/contrib/spatial/src/test/org/apache/lucene/spatial/geohash/TestGeoHashUtils.java b/contrib/spatial/src/test/org/apache/lucene/spatial/geohash/TestGeoHashUtils.java
new file mode 100644
index 00000000000..3bf531185e8
--- /dev/null
+++ b/contrib/spatial/src/test/org/apache/lucene/spatial/geohash/TestGeoHashUtils.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.geohash;
+
+import static junit.framework.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link GeoHashUtils}
+ */
+public class TestGeoHashUtils {
+
+ /**
+ * Pass condition: lat=42.6, lng=-5.6 should be encoded as "ezs42e44yx96",
+ * lat=57.64911 lng=10.40744 should be encoded as "u4pruydqqvj8"
+ */
+ @Test
+ public void testEncode() {
+ String hash = GeoHashUtils.encode(42.6, -5.6);
+ assertEquals("ezs42e44yx96", hash);
+
+ hash = GeoHashUtils.encode(57.64911, 10.40744);
+ assertEquals("u4pruydqqvj8", hash);
+ }
+
+ /**
+ * Pass condition: lat=52.3738007, lng=4.8909347 should be encoded and then
+ * decoded within 0.00001 of the original value
+ */
+ @Test
+ public void testDecodePreciseLongitudeLatitude() {
+ String hash = GeoHashUtils.encode(52.3738007, 4.8909347);
+
+ double[] latitudeLongitude = GeoHashUtils.decode(hash);
+
+ assertEquals(52.3738007, latitudeLongitude[0], 0.00001D);
+ assertEquals(4.8909347, latitudeLongitude[1], 0.00001D);
+ }
+
+ /**
+ * Pass condition: lat=84.6, lng=10.5 should be encoded and then decoded
+ * within 0.00001 of the original value
+ */
+ @Test
+ public void testDecodeImpreciseLongitudeLatitude() {
+ String hash = GeoHashUtils.encode(84.6, 10.5);
+
+ double[] latitudeLongitude = GeoHashUtils.decode(hash);
+
+ assertEquals(84.6, latitudeLongitude[0], 0.00001D);
+ assertEquals(10.5, latitudeLongitude[1], 0.00001D);
+ }
+
+ /*
+ * see https://issues.apache.org/jira/browse/LUCENE-1815 for details
+ */
+ @Test
+ public void testDecodeEncode() {
+ String geoHash = "u173zq37x014";
+ assertEquals(geoHash, GeoHashUtils.encode(52.3738007, 4.8909347));
+ double[] decode = GeoHashUtils.decode(geoHash);
+ assertEquals(52.37380061d, decode[0], 0.000001d);
+ assertEquals(4.8909343d, decode[1], 0.000001d);
+
+ assertEquals(geoHash, GeoHashUtils.encode(decode[0], decode[1]));
+
+ geoHash = "u173";
+ decode = GeoHashUtils.decode("u173");
+ geoHash = GeoHashUtils.encode(decode[0], decode[1]);
+ assertEquals(decode[0], GeoHashUtils.decode(geoHash)[0], 0.000001d);
+ assertEquals(decode[1], GeoHashUtils.decode(geoHash)[1], 0.000001d);
+ }
+}
diff --git a/contrib/spatial/src/test/org/apache/lucene/spatial/geometry/TestDistanceUnits.java b/contrib/spatial/src/test/org/apache/lucene/spatial/geometry/TestDistanceUnits.java
new file mode 100644
index 00000000000..114d88d387b
--- /dev/null
+++ b/contrib/spatial/src/test/org/apache/lucene/spatial/geometry/TestDistanceUnits.java
@@ -0,0 +1,46 @@
+package org.apache.lucene.spatial.geometry;
+
+import static junit.framework.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link org.apache.lucene.spatial.geometry.DistanceUnits}
+ */
+public class TestDistanceUnits {
+
+ /**
+ * Pass condition: When finding the DistanceUnit for "km", KILOMETRES is found. When finding the DistanceUnit for
+ * "miles", MILES is found.
+ */
+ @Test
+ public void testFindDistanceUnit() {
+ assertEquals(DistanceUnits.KILOMETERS, DistanceUnits.findDistanceUnit("km"));
+ assertEquals(DistanceUnits.MILES, DistanceUnits.findDistanceUnit("miles"));
+ }
+
+ /**
+ * Pass condition: Searching for the DistanceUnit of an unknown unit "mls" should throw an IllegalArgumentException.
+ */
+ @Test
+ public void testFindDistanceUnit_unknownUnit() {
+ try {
+ DistanceUnits.findDistanceUnit("mls");
+ assertTrue("IllegalArgumentException should have been thrown", false);
+ } catch (IllegalArgumentException iae) {
+ // Expected
+ }
+ }
+
+ /**
+ * Pass condition: Converting between the same units should not change the value. Converting from MILES to KILOMETRES
+ * involves multiplying the distance by the ratio, and converting from KILOMETRES to MILES involves dividing by the ratio
+ */
+ @Test
+ public void testConvert() {
+ assertEquals(10.5, DistanceUnits.MILES.convert(10.5, DistanceUnits.MILES), 0D);
+ assertEquals(10.5, DistanceUnits.KILOMETERS.convert(10.5, DistanceUnits.KILOMETERS), 0D);
+ assertEquals(10.5 * 1.609344, DistanceUnits.KILOMETERS.convert(10.5, DistanceUnits.MILES), 0D);
+ assertEquals(10.5 / 1.609344, DistanceUnits.MILES.convert(10.5, DistanceUnits.KILOMETERS), 0D);
+ }
+}