sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1490712 - in /sis/branches/JDK7/storage/sis-storage/src: main/java/org/apache/sis/index/GeoHashCoder.java test/java/org/apache/sis/index/GeoHashCoderTest.java test/java/org/apache/sis/test/suite/StorageTestSuite.java
Date Fri, 07 Jun 2013 16:36:45 GMT
Author: desruisseaux
Date: Fri Jun  7 16:36:45 2013
New Revision: 1490712

URL: http://svn.apache.org/r1490712
Log:
Make the GeoHashCoe methods non-static for allowing configuration in a future version.
Replaced the HashMap by a more compact and presumed efficient array of bytes.
Upgrated the test case from JUnit 3 to JUnit 4 and integrate with other SIS tests.

We will need an other pass on GeoHashCoder later for adding setter methods,
localizing the error messages (maybe IllegalArgumentException should be ParseException),
replace the double[] array by DirectPosition.

Modified:
    sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/index/GeoHashCoder.java
    sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/index/GeoHashCoderTest.java
    sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/index/GeoHashCoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/index/GeoHashCoder.java?rev=1490712&r1=1490711&r2=1490712&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/index/GeoHashCoder.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/index/GeoHashCoder.java
[UTF-8] Fri Jun  7 16:36:45 2013
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -14,130 +14,159 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.sis.index;
 
-//JDK imports
-import java.util.HashMap;
-import java.util.Map;
 
 /**
- * Utilities for encoding and decoding geohashes. Based on
- * http://en.wikipedia.org/wiki/Geohash.
+ * Utilities for encoding and decoding geohashes.
+ *
+ * @author  Chris Mattmann (JPL)
+ * @since   0.1
+ * @version 0.3
+ * @module
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Geohash">Wikipedia: Geohash</a>
  */
 public class GeoHashCoder {
-
-  private static final char[] BASE_32 = { '0', '1', '2', '3', '4', '5', '6',
-      '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n',
-      'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
-
-  private final static Map<Character, Integer> DECODE_MAP = new HashMap<Character,
Integer>();
-
-  private static final int PRECISION = 12;
-  private static final int[] BITS = { 16, 8, 4, 2, 1 };
-
-  static {
-    for (int i = 0; i < BASE_32.length; i++) {
-      DECODE_MAP.put(Character.valueOf(BASE_32[i]), Integer.valueOf(i));
-    }
-  }
-
-  private GeoHashCoder() {
-  }
-
-  /**
-   * Encodes the given latitude and longitude into a geohash
-   *
-   * @param latitude
-   *          Latitude to encode
-   * @param longitude
-   *          Longitude to encode
-   * @return Geohash encoding of the longitude and latitude
-   */
-  public static String encode(double latitude, double longitude) {
-    double[] latInterval = { -90.0, 90.0 };
-    double[] lngInterval = { -180.0, 180.0 };
-
-    final StringBuilder geohash = new StringBuilder();
-    boolean isEven = true;
-
-    int bit = 0;
-    int ch = 0;
-
-    while (geohash.length() < PRECISION) {
-      double mid = 0.0;
-      if (isEven) {
-        mid = (lngInterval[0] + lngInterval[1]) / 2D;
-        if (longitude > mid) {
-          ch |= BITS[bit];
-          lngInterval[0] = mid;
-        } else {
-          lngInterval[1] = mid;
-        }
-      } else {
-        mid = (latInterval[0] + latInterval[1]) / 2D;
-        if (latitude > mid) {
-          ch |= BITS[bit];
-          latInterval[0] = mid;
-        } else {
-          latInterval[1] = mid;
+    /**
+     * The characters used for the 32 bits variant of geohash.
+     */
+    private static final byte[] ENCODING_32 = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm',
+        'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
+    };
+
+    /**
+     * The reverse of {@link #ENCODING_32}.
+     */
+    private static final byte[] DECODING_32 = reverse(ENCODING_32);
+
+    /**
+     * Creates a decoding array from the given encoding one.
+     * Only the letters are stored in this array, not the digits.
+     */
+    private static byte[] reverse(final byte[] encoding) {
+        final byte[] decoding = new byte[26];
+        for (byte i=10; i<encoding.length; i++) {
+            decoding[encoding[i] - 'a'] = i;
         }
-      }
-
-      isEven = !isEven;
+        return decoding;
+    }
 
-      if (bit < 4) {
-        bit++;
-      } else {
-        geohash.append(BASE_32[ch]);
-        bit = 0;
-        ch = 0;
-      }
+    /**
+     * Amount of letters or digits to format in the geohash.
+     */
+    private int precision;
+
+    private static final int[] BITS = { 16, 8, 4, 2, 1 };
+
+    /**
+     * Creates a new geohash coder/decoder initialized to the default precision.
+     */
+    public GeoHashCoder() {
+        precision = 12;
     }
 
-    return geohash.toString();
-  }
+    /**
+     * Encodes the given latitude and longitude into a geohash.
+     *
+     * @param latitude
+     *          Latitude to encode
+     * @param longitude
+     *          Longitude to encode
+     * @return Geohash encoding of the longitude and latitude
+     */
+    public String encode(final double latitude, final double longitude) {
+      final double[] latInterval = { -90.0, 90.0 };
+      final double[] lngInterval = { -180.0, 180.0 };
 
-  /**
-   * Decodes the given geohash into a latitude and longitude
-   *
-   * @param geohash
-   *          Geohash to deocde
-   * @return Array with the latitude at index 0, and longitude at index 1
-   */
-  public static double[] decode(String geohash) {
-    final double[] latInterval = { -90.0, 90.0 };
-    final double[] lngInterval = { -180.0, 180.0 };
-
-    boolean isEven = true;
-
-    double latitude;
-    double longitude;
-    for (int i = 0; i < geohash.length(); i++) {
-      final int cd = DECODE_MAP.get(Character.valueOf(geohash.charAt(i)))
-          .intValue();
+      final StringBuilder geohash = new StringBuilder();
+      boolean isEven = true;
 
-      for (int mask : BITS) {
+      int bit = 0;
+      int ch = 0;
+
+      while (geohash.length() < precision) {
+        double mid;
         if (isEven) {
-          if ((cd & mask) != 0) {
-            lngInterval[0] = (lngInterval[0] + lngInterval[1]) / 2D;
-          } else {
-            lngInterval[1] = (lngInterval[0] + lngInterval[1]) / 2D;
-          }
+            mid = (lngInterval[0] + lngInterval[1]) / 2D;
+            if (longitude > mid) {
+                ch |= BITS[bit];
+                lngInterval[0] = mid;
+            } else {
+                lngInterval[1] = mid;
+            }
         } else {
-          if ((cd & mask) != 0) {
-            latInterval[0] = (latInterval[0] + latInterval[1]) / 2D;
-          } else {
-            latInterval[1] = (latInterval[0] + latInterval[1]) / 2D;
-          }
+            mid = (latInterval[0] + latInterval[1]) / 2D;
+            if (latitude > mid) {
+                ch |= BITS[bit];
+                latInterval[0] = mid;
+            } else {
+                latInterval[1] = mid;
+            }
         }
+
         isEven = !isEven;
-      }
 
+        if (bit < 4) {
+            bit++;
+        } else {
+            geohash.append((char) ENCODING_32[ch]);
+            bit = 0;
+            ch = 0;
+            }
+        }
+        return geohash.toString();
     }
-    latitude = (latInterval[0] + latInterval[1]) / 2D;
-    longitude = (lngInterval[0] + lngInterval[1]) / 2D;
 
-    return new double[] { latitude, longitude };
-  }
-}
\ No newline at end of file
+    /**
+     * Decodes the given geohash into a latitude and longitude
+     *
+     * @param geohash
+     *          Geohash to decode
+     * @return Array with the latitude at index 0, and longitude at index 1
+     */
+    public double[] decode(final String geohash) {
+        final int length = geohash.length();
+        final double[] latInterval = { -90.0, 90.0 };
+        final double[] lngInterval = { -180.0, 180.0 };
+        boolean isEven = true;
+
+        int nc;
+        for (int i=0; i<length; i+=nc) {
+            int c = geohash.codePointAt(i);
+            nc = Character.charCount(c);
+            if (c >= 'a' && c <= 'z') {
+                c = DECODING_32[c - 'a'];
+                if (c == 0) {
+                    throw new IllegalArgumentException(geohash);
+                }
+            } else if (c >= '0' && c <= '9') {
+                c -= '0';
+            } else {
+                throw new IllegalArgumentException(geohash);
+            }
+            for (int mask : BITS) {
+                if (isEven) {
+                    if ((c & mask) != 0) {
+                        lngInterval[0] = (lngInterval[0] + lngInterval[1]) / 2D;
+                    } else {
+                        lngInterval[1] = (lngInterval[0] + lngInterval[1]) / 2D;
+                    }
+                } else {
+                    if ((c & mask) != 0) {
+                        latInterval[0] = (latInterval[0] + latInterval[1]) / 2D;
+                    } else {
+                        latInterval[1] = (latInterval[0] + latInterval[1]) / 2D;
+                    }
+                }
+                isEven = !isEven;
+            }
+        }
+        double latitude = (latInterval[0] + latInterval[1]) / 2D;
+        double longitude = (lngInterval[0] + lngInterval[1]) / 2D;
+
+        return new double[] { latitude, longitude };
+    }
+}

Modified: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/index/GeoHashCoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/index/GeoHashCoderTest.java?rev=1490712&r1=1490711&r2=1490712&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/index/GeoHashCoderTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/index/GeoHashCoderTest.java
[UTF-8] Fri Jun  7 16:36:45 2013
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -14,123 +14,85 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.sis.index;
 
-import junit.framework.TestCase;
-
-//JDK imports
 import java.util.Map;
 import java.util.HashMap;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
 
 /**
- * Tests methods from the {@link GeoHashUtils} class.
+ * Tests methods from the {@link GeoHashCoder} class.
  *
- * @author rlaidlaw
+ * @author  Ross Laidlaw
+ * @since   0.1
+ * @version 0.3
+ * @module
  */
-public class GeoHashCoderTest extends TestCase
-{
-  private static final double EPSILON = 0.000001;
-  private HashMap<String, HashMap<String, double[]>> places;
-
-  /**
-   * Sets up objects before running tests.
-   */
-  public void setUp()
-  {
-    // Test data - locations and geohashes of some notable places
-    places = new HashMap<String, HashMap<String, double[]>>();
-    places.put("Empire State Building", new HashMap<String, double[]>());
-    places.put("Statue Of Liberty", new HashMap<String, double[]>());
-    places.put("The White House", new HashMap<String, double[]>());
-    places.put("Hoover Dam", new HashMap<String, double[]>());
-    places.put("Golden Gate Bridge", new HashMap<String, double[]>());
-    places.put("Mount Rushmore", new HashMap<String, double[]>());
-    places.put("Space Needle", new HashMap<String, double[]>());
-
-    places.get("Empire State Building").put("dr5ru6j2c62q", new double[]{40.748433, -73.985656});
-    places.get("Statue Of Liberty").put("dr5r7p4rx6kz", new double[]{40.689167, -74.044444});
-    places.get("The White House").put("dqcjqcpeq70c", new double[]{38.897669, -77.03655});
-    places.get("Hoover Dam").put("9qqkvh6mzfpz", new double[]{36.015556, -114.737778});
-    places.get("Golden Gate Bridge").put("9q8zhuvgce0m", new double[]{37.819722, -122.478611});
-    places.get("Mount Rushmore").put("9xy3teyv7ke4", new double[]{43.878947, -103.459825});
-    places.get("Space Needle").put("c22yzvh0gmfy", new double[]{47.620400, -122.349100});
-  }
-
-  /**
-   * Clears up objects after running tests.
-   */
-  public void tearDown()
-  {
-    places = null;
-  }
-
-  /**
-   * Tests the encode() method.
-   */
-  public void testEncode()
-  {
-    for (Map.Entry<String, HashMap<String, double[]>> place : places.entrySet())
-    {
-      HashMap<String, double[]> geoData = place.getValue();
-
-      if (geoData == null)
-      {
-        fail("incorrect test data");
-      }
-      else
-      {
-        for (Map.Entry<String, double[]> geoInfo : geoData.entrySet())
-        {
-          String geoHash = geoInfo.getKey();
-          double[] point = geoInfo.getValue();
-
-          if (geoHash == null || point == null)
-          {
-            fail("incorrect test data");
-          }
-          else
-          {
-            String encoded = GeoHashCoder.encode(point[0], point[1]);
-            assertEquals(geoHash, encoded);
-          }
+public final strictfp class GeoHashCoderTest extends TestCase {
+    /**
+     * Tolerance factor for floating point comparison.
+     */
+    private static final double TOLERANCE = 0.000001;
+
+    /**
+     * A geographic coordinates together with the expected geohash.
+     */
+    private static final class Place {
+        final double latitude;
+        final double longitude;
+        final String geohash;
+
+        Place(final double latitude, final double longitude, final String geohash) {
+            this.latitude  = latitude;
+            this.longitude = longitude;
+            this.geohash   = geohash;
+        }
+    }
+
+    /**
+     * Returns a map of places with their expected geohash.
+     */
+    private Map<String, Place> places() {
+        final Map<String, Place> places = new HashMap<>(12);
+        places.put("Empire State Building", new Place(40.748433,  -73.985656, "dr5ru6j2c62q"));
+        places.put("Statue Of Liberty",     new Place(40.689167,  -74.044444, "dr5r7p4rx6kz"));
+        places.put("The White House",       new Place(38.897669,  -77.036550, "dqcjqcpeq70c"));
+        places.put("Hoover Dam",            new Place(36.015556, -114.737778, "9qqkvh6mzfpz"));
+        places.put("Golden Gate Bridge",    new Place(37.819722, -122.478611, "9q8zhuvgce0m"));
+        places.put("Mount Rushmore",        new Place(43.878947, -103.459825, "9xy3teyv7ke4"));
+        places.put("Space Needle",          new Place(47.620400, -122.349100, "c22yzvh0gmfy"));
+        return places;
+    }
+
+    /**
+     * Tests the {@link GeoHashCoder#encode(double, double)} method.
+     */
+    @Test
+    public void testEncode() {
+        final GeoHashCoder coder = new GeoHashCoder();
+        for (final Map.Entry<String, Place> entry : places().entrySet()) {
+            final Place place = entry.getValue();
+            assertEquals(entry.getKey(), place.geohash,
+                    coder.encode(place.latitude, place.longitude));
         }
-      }
     }
-  }
 
-  /**
-   * Tests the decode() method.
-   */
-  public void testDecode()
-  {
-    for (Map.Entry<String, HashMap<String, double[]>> place : places.entrySet())
-    {
-      HashMap<String, double[]> geoData = place.getValue();
-
-      if (geoData == null)
-      {
-        fail("incorrect test data");
-      }
-      else
-      {
-        for (Map.Entry<String, double[]> geoInfo : geoData.entrySet())
-        {
-          String geoHash = geoInfo.getKey();
-          double[] point = geoInfo.getValue();
-
-          if (geoHash == null || point == null)
-          {
-            fail("incorrect test data");
-          }
-          else
-          {
-            double[] decoded = GeoHashCoder.decode(geoHash);
-            assertEquals(point[0], decoded[0], EPSILON);
-            assertEquals(point[1], decoded[1], EPSILON);
-          }
+    /**
+     * Tests the {@link GeoHashCoder#decode(String)} method.
+     */
+    @Test
+    public void testDecode() {
+        final GeoHashCoder coder = new GeoHashCoder();
+        for (final Map.Entry<String, Place> entry : places().entrySet()) {
+            final String name = entry.getKey();
+            final Place place = entry.getValue();
+            final double[] result = coder.decode(place.geohash);
+            assertEquals(name, place.latitude,  result[0], TOLERANCE);
+            assertEquals(name, place.longitude, result[1], TOLERANCE);
         }
-      }
     }
-  }
 }

Modified: sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java?rev=1490712&r1=1490711&r2=1490712&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
[UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
[UTF-8] Fri Jun  7 16:36:45 2013
@@ -33,7 +33,8 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.storage.IOUtilitiesTest.class,
     org.apache.sis.internal.storage.ChannelDataInputTest.class,
     org.apache.sis.internal.storage.ChannelImageInputStreamTest.class,
-    org.apache.sis.storage.StorageConnectorTest.class
+    org.apache.sis.storage.StorageConnectorTest.class,
+    org.apache.sis.index.GeoHashCoderTest.class
 })
 public final strictfp class StorageTestSuite extends TestSuite {
     /**



Mime
View raw message