All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.guerlab.spring.commons.geo.Geohash Maven / Gradle / Ivy

package net.guerlab.spring.commons.geo;

import java.math.BigDecimal;

public class Geohash {

    public static int BITS[] = {
            16, 8, 4, 2, 1
    };

    public static String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";

    public static int RIGHT = 0;

    public static int LEFT = 1;

    public static int TOP = 2;

    public static int BOTTOM = 3;

    public static int EVEN = 0;

    public static int ODD = 1;

    public static String[][] NEIGHBORS;

    public static String[][] BORDERS;

    static {
        NEIGHBORS = new String[4][2];
        BORDERS = new String[4][2];

        NEIGHBORS[BOTTOM][EVEN] = "bc01fg45238967deuvhjyznpkmstqrwx";
        NEIGHBORS[TOP][EVEN] = "238967debc01fg45kmstqrwxuvhjyznp";
        NEIGHBORS[LEFT][EVEN] = "p0r21436x8zb9dcf5h7kjnmqesgutwvy";
        NEIGHBORS[RIGHT][EVEN] = "14365h7k9dcfesgujnmqp0r2twvyx8zb";

        BORDERS[BOTTOM][EVEN] = "bcfguvyz";
        BORDERS[TOP][EVEN] = "0145hjnp";
        BORDERS[LEFT][EVEN] = "prxz";
        BORDERS[RIGHT][EVEN] = "028b";

        NEIGHBORS[BOTTOM][ODD] = NEIGHBORS[LEFT][EVEN];
        NEIGHBORS[TOP][ODD] = NEIGHBORS[RIGHT][EVEN];
        NEIGHBORS[LEFT][ODD] = NEIGHBORS[BOTTOM][EVEN];
        NEIGHBORS[RIGHT][ODD] = NEIGHBORS[TOP][EVEN];

        BORDERS[BOTTOM][ODD] = BORDERS[LEFT][EVEN];
        BORDERS[TOP][ODD] = BORDERS[RIGHT][EVEN];
        BORDERS[LEFT][ODD] = BORDERS[BOTTOM][EVEN];
        BORDERS[RIGHT][ODD] = BORDERS[TOP][EVEN];
    }

    private static void refineInterval(
            double[] interval,
            int cd,
            int mask) {
        if ((cd & mask) > 0) {
            interval[0] = (interval[0] + interval[1]) / 2.0;
        } else {
            interval[1] = (interval[0] + interval[1]) / 2.0;
        }
    }

    public static String calculateAdjacent(
            final String hash,
            int dir) {
        String srcHash = hash.toLowerCase();

        char lastChr = srcHash.charAt(srcHash.length() - 1);
        int type = srcHash.length() % 2 == 1 ? ODD : EVEN;

        String base = srcHash.substring(0, srcHash.length() - 1);

        if (BORDERS[dir][type].indexOf(lastChr) != -1) {
            base = calculateAdjacent(base, dir);
        }

        return base + BASE32.charAt(NEIGHBORS[dir][type].indexOf(lastChr));
    }

    /**
     * 获取相邻的geohash
     *
     * @param geohash
     *            geohash
     * @param length
     *            长度
     * @return 相邻的geohash
     */
    public static GeoHashExpand getGeoHashExpand(
            final String geohash,
            int length) {
        int maxLength = Math.min(geohash.length(), length);

        String hash = geohash.substring(0, maxLength);

        String geohashTop = calculateAdjacent(hash, TOP);
        String geohashBottom = calculateAdjacent(hash, BOTTOM);
        String geohashRight = calculateAdjacent(hash, RIGHT);
        String geohashLeft = calculateAdjacent(hash, LEFT);

        String geohashTopLeft = calculateAdjacent(geohashLeft, TOP);
        String geohashTopRight = calculateAdjacent(geohashRight, TOP);
        String geohashBottomRight = calculateAdjacent(geohashRight, BOTTOM);
        String geohashBottomLeft = calculateAdjacent(geohashLeft, BOTTOM);

        GeoHashExpand expand = new GeoHashExpand(hash, geohashTop, geohashBottom, geohashRight, geohashLeft,
                geohashTopLeft, geohashTopRight, geohashBottomRight, geohashBottomLeft);

        return expand;
    }

    /**
     * 解析
     *
     * @param geohash
     *            geohash
     * @return 地理对象
     */
    public static Geo decode(
            String geohash) {
        boolean even = true;
        double[] lat = new double[3];
        double[] lon = new double[3];

        lat[0] = -90.0;
        lat[1] = 90.0;
        lon[0] = -180.0;
        lon[1] = 180.0;

        for (int i = 0; i < geohash.length(); i++) {
            char c = geohash.charAt(i);
            int cd = BASE32.indexOf(c);
            for (int mask : BITS) {
                if (even) {
                    refineInterval(lon, cd, mask);
                } else {
                    refineInterval(lat, cd, mask);
                }
                even = !even;
            }
        }
        lat[2] = (lat[0] + lat[1]) / 2.0;
        lon[2] = (lon[0] + lon[1]) / 2.0;

        return new Geo(new BigDecimal(lat[2]), new BigDecimal(lon[2]));
    }

    /**
     * 编码geohash
     *
     * @param latitude
     *            经度
     * @param longitude
     *            经度
     * @return geohash
     */
    public static String encode(
            BigDecimal latitude,
            BigDecimal longitude) {
        return encode(latitude.doubleValue(), longitude.doubleValue());
    }

    /**
     * 编码geohash
     *
     * @param latitude
     *            经度
     * @param longitude
     *            经度
     * @return geohash
     */
    public static String encode(
            double latitude,
            double longitude) {
        boolean even = true;
        double lat[] = new double[3];
        double lon[] = new double[3];
        int bit = 0;
        int ch = 0;
        int precision = 12;
        String geohash = "";

        lat[0] = -90.0;
        lat[1] = 90.0;
        lon[0] = -180.0;
        lon[1] = 180.0;

        while (geohash.length() < precision) {
            if (even) {
                double mid = (lon[0] + lon[1]) / 2.0;
                if (longitude > mid) {
                    ch |= BITS[bit];
                    lon[0] = mid;
                } else {
                    lon[1] = mid;
                }
            } else {
                double mid = (lat[0] + lat[1]) / 2.0;
                if (latitude > mid) {
                    ch |= BITS[bit];
                    lat[0] = mid;
                } else {
                    lat[1] = mid;
                }
            }
            even = !even;
            if (bit < 4) {
                bit++;
            } else {
                geohash += BASE32.charAt(ch);
                bit = 0;
                ch = 0;
            }
        }
        return geohash;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy