一个点是否在矩形内的算法_空间点索引算法-GeoHash

a27d52ad0a55af0ce74f0ae8c6a3380b.png

在移动互联网领域有很多结合地理位置的应用,比如利用百度地图查找附近3公里的美食,滴滴打车为用户匹配附近的车辆等等。当然在实际的业务中除了距离之外要考虑很多其它的因素,比如寻找附近的美食可能还要考虑用户的饮食喜好、搜索时间(早上、中午和下午推荐不同的搜索结果)、餐馆的用户评价、信用等级等等;匹配附近的车辆可能还要考虑车辆的类型、司机的星级等等。

本文只考虑距离因素,考虑打车的场景,如何找出距离自己5公里范围内所有匹配的司机呢?

708d9a651bed36ecca2e49b4ecda82e7.png

直接的思路

最直观的想法就是把所有在线车辆的位置都存储在数据库里面(不考虑车辆的在线和离线是实时变化的,也暂不考虑车辆的位置一直是动态变化的),每次用户发起打车请求的时候,从数据库里面读取所有在线车辆的位置,计算车辆与用户位置的距离,把所有小于5公里的车辆筛选出来。

这种做法比较粗暴,实际的业务实现也不会这么做,因为实际车辆数量是巨大的,逐个计算距离进行筛选带来的时间耗费是不能容忍的。

既然数据量太大,并且打车业务也是有区域性的,比如用户不可能在北京呼叫深圳的快车,所以分而治之,对地图进行分块可以很大程度上可以解决实际业务耗时的问题。

1f313905a2fecf769cfb8bfbc87db0f6.png

地图分块的过程其实就是一种添加索引的过程,如果能想到一个办法,把地图上的点添加一个合适的索引,并且能够排序,那么就可以利用类似二分查找的方法进行快速查询,GeoHash就是其中一个比较通用的空间索引方法。

GeoHash算法

GeoHash是一种空间地址编码方法,它能够把二维的空间经纬度数据编码成一个字符串。比如下图展示了北京9个区域的GeoHash字符串,分别是WX4ER,WX4G2、WX4G3等,每一个字符串代表了某一矩形区域,矩形区域内所有的点都共享相同的GeoHash字符串。这样可以把位置落在某个区域内的所有车辆都以该区域的GeoHash为Key缓存起来,当用户的发送打车需求后,只通过GeoHash计算就可以迅速定位到该区域内的车辆。

78c65b67317595e67a32a1ba4342dfe7.png

Geohash的字符串长短来决定要划分区域的大小,Geohash能够提供任意精度的分段级别,一般分级从 1-12 级,对照下表,一旦选定 cell 的宽和高,则Geohash字符串的长度就确定了,这样就把地图分成一个个的矩形区域。

e420bb7bd259ec641585543027ed1122.png

把地图区域划分好之后,如何快速的查找一个点附近邻近的点和区域呢?一个点附近的地方的 hash 字符串有公共前缀,并且公共前缀的长度越长,这两个点距离越近。利用这个特性,可以用来快速的进行邻近点的搜索。越接近的点通常和目标点的 Geohash 字符串公共前缀越长(也有特殊情况,需要单独处理)。

GEO字符串的生成

第一步:地图网格化。假设通过查表,字符串长度为6的矩形可以满足业务需求。

假设待查询的经纬度是[31.1932993, 121.43960190000007]。

第二步:处理经纬度

先处理维度,纬度区间是[-90,90]。把这个区间分为2部分,即[-90,0),[0,90]。31.1932993位于(0,90]区间,即右区间,标记为1。然后继续把(0,90]区间二分,分为[0,45),[45,90],31.1932993位于[0,45)区间,即左区间,标记为0。一直划分下去。

1b83348cf6d9dc1c71522382c972d99d.png

再处理经度,经度区间是[-180,180],处理方式与经度相同。

783937a6f6a5f0b38b4b601cc4438a6e.png

最终纬度产生的二进制是101011000101110,经度产生的二进制是110101100101101。

第三步,重新组合二进制串

按照“偶数位放经度,奇数位放纬度”的规则,重新组合经度和纬度的二进制串,生成新的:111001100111100000110011110110。

第四步:转换成字符串。通过查找对应的Base32表把这个最终的字符串转换成字符。11100 11001 11100 00011 00111 10110转换成十进制是 28 25 28 3 7 22,查表编码得到最终结果,wtw37q。

4476477e42260e7ec6c9c1cccd5d6be2.png

我们把网格周围8个网格都计算出来,如下图:

2e662389eb2f19b713ad9904d9eca0ed.png

可以看出这邻近的9个格子,前缀都完全一致,都是wtw37。

Geohash代码实现

8b5353f1e6e1be8d941a6ab3430f04c2.png

c048bfa85d8a2c972815612b787c0a32.png

43bd58e23b15f29e24934cd0602b855c.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值