php 根据经纬度查找附近的人_Python实现微信《附近的人》功能-Geohash算法编码和字典树查询...

本文解析了微信“附近的人”功能的技术实现原理,包括利用GeoHash算法进行经纬度编码及组码,使用字典树存储和查询编码后的地理位置信息,以及通过Haversine公式计算距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5a029ecec62888952dac88a176b9efef.png

1前言

在微信轻触【发现】->【附近的人】,便可以查看距离自己较近的人,这个神奇的功能让陌生人能成为朋友、饭友、聊友、约友等。但是这个功能的技术实现原理又是什么呢?

最简单的想法是计算自己和每个好友间的距离,然后按距离排序并输出。这种想法对于用户数较少的应用尚可,但对于微信这种有上亿用户数的应用,显然是不可取的。事实上,不管用户数多少,只要点击【附近的人】基本上几秒钟之内,就能把附近的用户筛选出来。这么快的速度是用了什么奇淫巧技呢?本文我们一起来探究下。

2原理

首先需要获取自己和好友的位置信息,这涉及到LBS(Location Based Service,基于位置的服务),就是通过移动终端获取到用户或者物体的经纬度坐标,通过这些位置信息来提供服务。

其次需要对这些位置信息进行网格化处理。把整个地球想象成经纬度构成的网络,每一个网格包含一定的区域,只要这个网格划分足够精细,便可以用来逼近我们所处的位置了。当某个用户都被划分在某个网格中时,通过查询自己所处网格或周边网格内的用户,便可以找到自己附近的人。

所以需要记录下网格的名称,以便于查询用户附近的人。记录网格名称的过程涉及到GeoHash算法。

当网格划分结束(即每个网格都确定了名称),那么接下来便是根据网格名称来查询用户自己的附近的人。本文用到了字典树来实现这个查询功能。

3实现流程

本文主要介绍如何在Python中实现模拟微信《附近的人》这个功能。实现流程如下所示。

b87b55e77dbe10ba6774cd2e23c1ebee.png
微信《附近的人》模拟实现流程

4 Geohash法

利用GeoHash算法,可以将经纬度数据转化为字符串格式,这样便将二维的数据转化为了一维,存储就方便了,搜索效率也会高很多。GeoHash算法分为编码和组码。

第一步是编码。经纬度的编码通过折半比较法实现,当大于中值时编码为1,下次新的区间为中值到最大值;当小于中值时编码为0,下次新的区间为最小值到中值。这样一直比较下去,直到到达要求的精度,经度和纬度的方法是一样的,只是纬度的原始区间是[-90,90],经度的原始区间[-180,180]。如下表所示对纬度42.61233和经度-5.61234进行编码:

b553e57095477f1cdf09f37525af7073.png

这样便可将经纬度(42.61233, -5.61234)转换成二进制码(10111 10010, 01111 10000),二进制码的位数即为编码精度。

第二步是组码(或者合并)。将第一步产生的二进制码组合起来产生Base32的字符串。组码的方式是奇数位放经度、偶数位放纬度(也可互换),如下图所示:

cc99f073aeee1c9735eaee2426cdb2c4.png

合并后的二进制码为:01101, 11111, 11000, 00100

将二进制码转换成十进制数,得到:13, 31, 24, 4

再按照如下图所示的Base32编码转换关系(用0-9、b-z(去掉a, i, l, o)这32个字母表示),将十进制数转换为Base32的字符串编码,得到ezs4

f5070a8445f856ff24dfcc2666763a33.png

本例中编码精度设置为10位,当继续增大精度位数时,可得到更为精确的Base32字符串。

给定如下图所示的8个用户位置信息,

20d1769b3f4ba9dde54d1e68cb1c7cbd.png
用户的所有好友的位置信息

可以利用Geohash算法将这些好友的经纬度信息转化为Base32的字符串编码。如下表所示:

2eecdf8d48993b9e757cbfed287cf7b4.png

其中,转换后的后4位为_ID,是为了区别同一位置的不同用户。

用Python实现经纬度的编码和组码,代码见附录一。

5 字典树查询法

计算出这些经纬度的字符串编码值,那么如何存放到数据库中,能够快速检索一个用户附近的好友呢?

本节使用字典树来完成字符串编码值的存储与查询。

字典树(也叫Trie树),是一种 N 叉树,也是一种特殊的前缀树结构。通常来说,一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个字符。每一个节点会有多个子节点,通往不同子节点的路径上有着不同的字符。子节点代表的字符是由节点本身的原始字符,以及通往该子节点路径上所有的字符组成的。

前缀树的一个重要的特性是,节点所有的后代都与该节点相关的字符串有着共同的前缀。

将上述各个用户转换后的字符串编码值,存入到字典树中,如下图所示:

03404c88924ce361bfe36b5dcf4cf79b.png

图中,每一条从根节点(root)到叶子结点的路径都表示一个用户的位置信息(经纬度)。

当构建完成字典树后,便可以查询离自己距离较近的用户是哪些。如当自己(ID记为109)的位置信息表示为:(42.61236, -5.61234)时,通过Geohash算法可以计算出字符串编码值为 ‘ezs42m34yzx_109’。

通过字典树查询(深度优先搜索),可以获取到离该用户最近的好友有100和107,他们的信息分别如下:

2385110785f1ef48062e5f605fab3cea.png

用Python实现字典树的插入与查询,代码见附录二。

6 距离计算法

根据2个经纬度点,可以利用Haversine公式计算这2个经纬度点之间的距离,计算公式如下:

其中

其中R为地球半径,可取平均值 6371km;

表示两点的纬度;
表示两点的经度。

可以使用字典dict来存放用户自己到每个附近邻居的距离。每个用户都可以通过ID检索,于是可以把ID作为字典的键,把距离作为字典的值。

然后通过快速排序方法对字典排序(如对上述ID=100和ID=107的用户按距离排序),便可以按照距离筛选出离自己较近的好友了。。。

7 参考

[1] https://blog.csdn.net/wzw212/article/details/79308798

[2] https://blog.csdn.net/qfire/article/details/83189482

[3] https://www.cnblogs.com/zhoug2020/p/8993750.html

附录一:用Python实现经纬度的编码、组码和解码

class 

附录二:用Python实现字典树的插入与查询

class 

感谢阅读。。。

少侠,留个印记再走呗。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值