GeoHash算法获取附近店铺和距离

认识GeoHash

GeoHash算法将二维经纬度坐标直接转换成字符串,每一个字符串代表一个矩形区域,也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,字符串的长度越大,矩形的区域就越小,经度也就越高。字符串相似的表示距离相近,这样可以利用字符串的前缀匹配来查询附近的POI信息

GeoHash算法的步骤

地球纬度区间是[-90,90],经度区间是[-180,180],通过区间法对经度和纬度分别进行计算,假如我们获取到的当前坐标为经度-0.12866, 纬度38.534413,以纬度为例:

  1. 将纬度平均分成两个区间:[-90,0),[0,90],成为左区间和右区间,可以判断出38.534413属于右区间,则值为1,(如果属于左区间则值为0);
  2. 接着将右区间继续划分,就变成了[0,45),[45,90],此时,38.534413属于左区间,则值为0
  3. 递归上述过程,则区间的值会越来越逼近38.534413
  4. 随着算法的进行,我们将会得到一个序列,序列的长度跟递归的次数有关,但是一定要保证的是经度和纬度的序列长度是一样的,我这里设置的递归长度是30,经度和纬度加起来就是60,
  5. 根据算法我们最终得到经度的序列为[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
    0],纬度的序列为[1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1,
    0],然后我们根据此序列再组合一个新的序列偶数位放经度,奇数位放纬度,把2串编码组合生成新串[0, 1, 1, 0, 1, 1, 1, 1,
    1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0,
    0],实际上这个序列是一串二进制
  6. 最后,我们将这个新串转换成十进制再使用0-9、b-z(去掉a, i, l, o)对这组十进制进行编码得到的字符串eyzgjnfr4p0p就是最终的GeoHash编码。下图是网上给出的不同编码长度给出的精度:

         
         

得到GeoHash的值之后可以同样的保存近数据库中,每次查询离我最近的数据的时候理论上来说可以根据精确度截取GeoHash的值进行模糊查询,但是这样查询是有问题的,因为你没法保证你每次查询时你的当前坐标刚好在这个矩形的正中间,如果你的坐标处在矩形的边界,那么你就无法获取其附近的数据,那么这个问题怎么解决呢?其实也很简单,以你当前所在位置的矩形为九宫格的中间格子,再获取其相邻的8个矩形。

获取其他8个区域的GeoHash也很简单,上面的表已经给出了每一个区域内经度和纬度的宽度,那么直接加减后就可以得出周边相邻的8个格子,我这里自己写了一套GeoHash的算法,得出当前坐标的GeoHash的值以及相邻8个格子的值,代码比较多就不贴上来了,在GitHub上有一个别人写好的JAVA版本的可以直接拿来用,叫geohash-java,貌似maven的仓库中也有

使用的方法也很简单:

/**
 * Created by linchaokun on 2018/4/9.
 */
public class GeohashUtil {
    private static  String format = "0.000000";
    private static final  double EARTH_RADIUS = 6371000;//赤道半径(单位m)
    private static int numberOfCharacters = 12;

    /**
     * 根据经纬值得到Geohash字串
     *
     * @param lat
     *            纬度值
     * @param lon
     *            经度值
     * @return Geohash字串
     */
    public static String encode(double lat, double lon) {
        return getGeoHash(lat,lon,numberOfCharacters).toBase32();

    }

    /**
     * 根据经纬值得到Geohash字串
     *
     * @param lat
     *            纬度值
     * @param lon
     *            经度值
     * @param number
     *            经度 1-12
     * @return Geohash字串
     */
    public static String encode(double lat, double lon,int number) {
        return getGeoHash(lat,lon,number).toBase32();

    }

    /**
     * 获取整个九宫格的GeoHash的值
     *
     * @param lat
     *            纬度值
     * @param lon
     *            经度值
     * @return Geohash字串集合
     */
    public static List<String> encodes(double lat, double lon){
        List<String> hashs = new ArrayList<>();
        GeoHash[] adjacent = getGeoHash(lat,lon,numberOfCharacters).getAdjacent();//获取整个九宫格的GeoHash的值

        for (GeoHash hash : adjacent) {
            hashs.add(hash.toBase32());
        }

        return hashs;
    }

    /**
     * 获取整个九宫格的GeoHash的值
     *
     * @param lat
     *            纬度值
     * @param lon
     *            经度值
     * @param number
     *            经度 1-12
     * @return Geohash字串集合
     */
    public static List<String> encodes(double lat, double lon,int number){
        List<String> hashs = new ArrayList<>();
        GeoHash[] adjacent = getGeoHash(lat,lon,number).getAdjacent();//获取整个九宫格的GeoHash的值

        for (GeoHash hash : adjacent) {
            hashs.add(hash.toBase32());
        }

        return hashs;
    }

    /**
     * 根据GeoHash的值转换为经纬度
     * @param geohash
     * @return
     */
    public static double[] decode(String geohash){

        GeoHash geoHash = GeoHash.fromGeohashString(geohash);
        WGS84Point point = geoHash.getPoint();
        double lat = point.getLatitude();
        double lon = point.getLongitude();

        DecimalFormat df = new DecimalFormat(format);

        return new double[]{Double.parseDouble(df.format(lat)),Double.parseDouble(df.format(lon))};
    }


    /**
     * 基于googleMap中的算法得到两经纬度之间的距离,计算精度与谷歌地图的距离精度差不多,相差范围在0.2米以下
     * @param lat1 第一点的精度
     * @param lng1 第一点的纬度
     * @param lat2 第二点的精度
     * @param lng2 第二点的纬度
     * @return 返回的距离,单位m
     * */

    public static double distance(double lat1, double lng1, double lat2, double lng2) {
        double x1 = Math.cos(lat1) * Math.cos(lng1);
        double y1 = Math.cos(lat1) * Math.sin(lng1);
        double z1 = Math.sin(lat1);
        double x2 = Math.cos(lat2) * Math.cos(lng2);
        double y2 = Math.cos(lat2) * Math.sin(lng2);
        double z2 = Math.sin(lat2);
        double lineDistance =
                Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
        double s = EARTH_RADIUS * Math.PI * 2 * Math.asin(0.5 * lineDistance) / 180;
        return Math.round(s * 10000) / 10000;
    }

    private static GeoHash getGeoHash(double lat, double lon,int number){
        DecimalFormat df = new DecimalFormat(format);
        return GeoHash.withCharacterPrecision(Double.parseDouble(df.format(lat)),Double.parseDouble(df.format(lon)),number);
    }



    public static void main(String []args){
        //116.402843,39.999375  鸟巢   wx4g8c9v
        //116.3967,39.99932    水立方   wx4g89tk
        //116.40382,39.918118   故宫  wx4g0ffe
        double lon1=116.402843;
        double lat1=39.999375;
        double lon2=116.40382;
        double lat2=39.918118;
        double dist;
        String geocode;
        List<String> hashs = new ArrayList<>();



        dist = distance(lat1,lon1,lat2,lon2);
        System.out.println("两点相距1:" + dist + " 米");

        hashs=encodes(lat1, lon1);
        System.out.println("当前位置编码:" + hashs.toString());

        hashs=encodes(lat2, lon2);
        System.out.println("远方位置编码:" + hashs.toString());

        double[] decode = GeohashUtil.decode(encode(lat1, lon1));
        System.out.println(decode[0]+","+decode[1]);

    }
}

转自 林朝昆博客

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Python中实现geohash算法,你可以使用geohash库。首先,你需要确保已经安装了geohash库。你可以使用pip命令进行安装,命令如下:pip install geohash。如果安装成功后,仍然无法导入geohash模块并提示ImportError: No module named 'geohash'的错误,你可以尝试以下方法进行修复:将Geohash文件名改为geohash,然后在geohash文件夹下的__init__.py文件中将from geohash import decode_exactly, decode, encode改为from .geohash import decode_exactly, decode, encode(在geohash前面加一个'.')。这样应该可以解决导入模块的问题。[1] 一旦你成功导入了geohash库,你就可以使用它来进行geohash算法的实现。例如,你可以使用decode_exactly函数来将geohash字符串解码为经度和纬度的坐标。例如,你可以使用以下代码来解码geohash字符串"wm6nc":print(geohash.decode_exactly("wm6nc")),这将返回一个包含经度、纬度、经度精度和纬度精度的元组。(30.73974609375, 104.12841796875, 0.02197265625, 0.02197265625)[2] geohash库还提供了其他功能模块,如距离度量和几何计算。距离度量模块提供了与距离相关的函数,如distance和dimensions。几何模块提供了将多边形转换为geohash列表的函数,如polygon_to_geohashgeohash_to_polygon。这些功能可以帮助你在地理区域中进行近似地理差异的计算。你可以使用shapely库进行几何计算[3]。 综上所述,要在Python中实现geohash算法,你可以使用geohash库,并根据需要使用其提供的不同功能模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值