GeoHash算法原理及实现

GeoHash是一种将经纬度坐标转化为字符串的技术,用于位置服务。算法通过递归划分地球区域并将其映射到二维空间,然后将经纬度转换为二进制,合并后使用Base32编码。在Java中,计算经纬度的二进制和编码过程是关键。然而,GeoHash存在精度问题,可能导致距离较近的点被忽略。为解决此问题,可以扩展搜索范围到相邻区域,并结合距离计算筛选附近的人。
摘要由CSDN通过智能技术生成

GeoHash算法原理

GeoHash是目前比较主流实现位置服务的技术,Geohash算法将经纬度二维数据编码为一个字符串,本质是一个降维的过程

样例数据(基于15次区域分割)

位置 经纬度 Geohash
北京站 116.433589,39.910508 wx4g19
天安门 116.403874,39.913884 wx4g0f
首都机场 116.606819,40.086109 wx4uj3

GeoHash算法思想

我们知道,经度范围是东经180到西经180,纬度范围是南纬90到北纬90,我们设定西经为负,南纬为负,所以地球上的经度范围就是[-180, 180],纬度范围就是[-90,90]。如果以本初子午线、赤道为界,地球可以分成4个部分。

GeoHash的思想就是将地球划分的四部分映射到二维坐标上。

[-90˚,0˚)代表0,(0˚,90˚]代表1,[-180˚,0)代表0,(0˚,180˚]代表1
映射到二维空间划分为四部分则如下图

在这里插入图片描述
但是这么粗略的划分没有什么意义,想要更精确的使用GeoHash就需要再进一步二分切割
在这里插入图片描述
通过上图可看出,进一步二分切割将原本大略的划分变为细致的区域划分,这样就会更加精确。GeoHash算法就是基于这种思想,递归划分的次数越多,所计算出的数据越精确。

GeoHash算法原理

GeoHash算法大体上分为三步:1. 计算经纬度的二进制、2. 合并经纬度的二进制、3. 通过Base32对合并后的二进制进行编码。

  1. 计算经纬度的二进制
	//根据经纬度和范围,获取对应的二进制
	private BitSet getBits(double l, double floor, double ceiling) {
   
		BitSet buffer = new BitSet(numbits);
		for (int i = 0; i < numbits; i++) {
   
			double mid = (floor + ceiling) / 2;
			if (l >= mid) {
   
				buffer.set(i);
				floor = mid;
			} else {
   
				ceiling = mid;
			}
		}
		return buffer;
	}

上述代码numbits为:private static int numbits = 3 * 5; //经纬度单独编码长度也就是说将地球进行15次二分切割

注: 这里需要对BitSet类进行一下剖析,没了解过该类的话指定懵。

了解BitSet只需了去了解它的set()、get()方法就足够了

  • BitSet的set方法
 /**
     * Sets the bit at the specified index to {@code true}.
     *
     * @param  bitIndex a bit index
     * @throws IndexOutOfBoundsException if the specified index is negative
     * @since  JDK1.0
     */
    public void set(int bitIndex) {
   
        if (bitIndex < 0)
            throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

        int wordIndex = wordIndex(bitIndex);
        expandTo(wordIndex);

        words[wordIndex] |= (1L << bitIndex); // Restores invariants

        checkInvariants();
    }

set方法内wordIndex(bitIndex)底层将bitIndex右移6位然后返回,ADDRESS_BITS_PER_WORD为常量6

/**
    * Given a bit index, return word index containing it.
    */
   private static int wordIndex(int bitIndex) {
   
       return bitIndex >> ADDRESS_BITS_PER_WORD;
   }

set方法内的expandTo(wordIndex)只是一个判断数组是否需要扩容的方法

/**
     * Ensures that the BitSet can accommodate a given wordIndex,
     * temporarily violating the invariants.  The caller must
     * restore the invariants before returning to the user,
     * possibly using recalculateWordsInUse().
     * @param wordIndex the index to be accommodated.
     */
    private void expandTo(int wordIndex) {
   
        int wordsRequired = wordIndex+1;
        if (wordsInUse < wordsRequired) {
   
            ensureCapacity(wordsRequired);
            wordsInUse = wordsRequired;
        }
    }

set内重要的一行代码words[wordIndex] |= (1L << bitIndex),这里只解释一下|=

a|=b就是a=a|b,就是说将a、b转为二进制按位与,同0为0,否则为1

  • BitSet的get方法
 /**
    * Returns the value of the bit with the specified index. The value
    * is {@code true} if the bit with the index {@code bitIndex}
    * is currently set in this {@code BitSet}; otherwise, the result
    * is {@code false}.
    *
    * @param  bitIndex   the bit index
    * @return the value of the bit with the specified index
    * @throws IndexOutOfBoundsException if the specified index is negative
    */
   public boolean get(int bitIndex) {
   
       if (bitIndex < 0)
           throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

       checkInvariants();

       int wordIndex = wordIndex(bitIndex);
       return (wordIndex < wordsInUse)
           && ((words[wordIndex] & (1L << bitIndex)) != 0);
   }

get方法用一句话概括就是:如果传入的下标有值,返回true;反之为false

以天安门坐标为例:39.913884, 116.403874

	BitSet latbits = getBits(lat, -90, 90);
   	BitSet lonbits = getBits(lon, -180, 180);
   	// 纬度
   	for (int i = 0; i < numbits; i++) {
   
   		System.out.print(latbits.get(i) + " ");
   	}
   	// 经度
    for (int i = 0; i < numbits; i++) {
   
   		System.out.print(lonbits.get(i) + " ");
   	}

纬度经过转换为:

true false true true true false false false true true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问题不太大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值