定位位置_是什么能让 APP 快速精准定位到我们的位置?

什么是geohash?它的原理是什么?它帮助我们解决了哪些棘手问题?这篇文章将向你解释。

本文包含以下内容。大约需要10分钟来完成阅读:

我们在日常生活中遇到什么定位场景

简要回顾经纬度

地理哈希原理来分析

地理哈希

中存在的边界问题如何解决边界问题

两点间距离的计算

地理哈希在再版中的实现

我们在日常生活中遇到的位置场景下面两张图片应该相互熟悉。打开位置,找到我附近的车。那么,这是如何实现的呢?

定位位置

我脑海中的第一个实现是实时报告经度和纬度在数据库中,经度和纬度被标记为索引,并且通过搜索和比较经度和纬度的值来找到距离为1公里的车辆。但是,这种方法索引多、值大,并且需要遍历经度和纬度,这使得查询速度慢且效率低。< br>

那么,如何准确定位和快速搜索这些应用程序?答案是geohash

geohash通过算法将位置的经度和纬度转换为散列字符串。如果两个位置更近,则它们的哈希值的前缀是相同的然后通过数据库中的类似操作符“like wtw366%”,可以快速找到附近的汽车

例如,上海腾讯大厦的经度和纬度是:(31.1688749,121.3975184),那么转换成地理哈希是wtw366ngz5qt。我们想找一辆附近的车,我们可以使用:选择*从卡特彼勒地理哈希喜欢' wtw 366% ';< br>
从购物车中选择* LEft(geo hash,6)=“wtw 366”;
简要回顾一下纬度和经度

在大致了解了geohash是什么之后,让我们回顾一下纬度和经度是什么(高中生可能已经忘记了光(逃逸)),这对理解geohash有很大帮助。

我们将铺平道路,并得到以下计划

定位位置

地球平面图

将地球分为经度和纬度,以赤道和本初子午线为边界。赤道在0度,本初子午线也在0度。赤道是经度x的横坐标,本初子午线是纬度y的纵坐标。

定位位置

经度和纬度地图经度(经度)`纬度)`液化天然气'和`纬度

的简称,其中从本初子午线向东180度称为东经,用" e": (0,180)表示;西经180度是西经,用“w”表示:十字坐标法

我们通常读“纬度和经度”,实际上,一个位置的书写纬度和经度是“(纬度,经度)”

例如,上海腾讯大厦的定位是:(31.1688749,121.3975184)意思是:纬度=31.1688749,经度=121.3975184

定位位置

上海腾讯大厦经纬度图的地理哈希原理分析

了解了什么是经纬度之后,我们现在可以开始讨论地理哈希原理了。geohash通过以下步骤将经度和纬度子字符串转换为哈希字符串

指定位置的经度和纬度坐标值

根据十字坐标图和二分法将纬度和经度分成1和0的二进制数字串。

根据“偶数表示经度,奇数表示纬度”的算法将经度和纬度两个二进制数字串组合在一起组合

后,二进制数字串自始至终每隔5位数转换成十进制数,少于5位数的用0填充

十进制数字,对应于base32字符串算法的位置,被逐个匹配,并且获得最终的字符串结果。

根据调度划分被截获,得到最终的geohash值

让我们按照这个顺序计算和运算,并结合实例。

1。查找位置的纬度和经度

我们可以使用各种地图和定位工具,如谷歌地图,通过定位或搜索位置来轻松查找纬度和经度。

定位位置

腾讯大厦经纬度

这样,我们发现上海腾讯大厦的经纬度为(31.1688749,121.3975184)

2。根据二进制算法,经纬度转换为01二进制

上海腾讯大厦经纬度为(31.1688749,121.3975184)

,范围为-90到-90。90)被分成两个部分(-90,0)和(0,90)。如果目标纬度位于上一部分,则代码为0,否则代码为1

被编码为1,因为31.1688749属于(0,90)

然后将(0,90)分成(0,45),(45,90)两个区间,而31.1688749位于(0,45),因此代码为0

然后将(0,45)分成(0,22.5),(22.5,45)两部分,而31.1688749位于(22.5,45),因此代码为1< br>…。< br>….

等,上海腾讯大厦的纬度代码可以如下获得:

101011000101100011111101101

经度用同样的算法细分为(-180,180),(-180,0),(0,180),代码为

110101100101001001110111010
$ maxLat = 90< br>
//经度
$ MinLng =-180;
$ maxLng = 180< br>
//两点搜索计算,将经度和纬度转换为二进制数字字符串
$ latLength = $ LNglength = 0;
$ latList = $ lngList =

/$ precision的最大精度为12,表示12个字符,一个字符由5个二进制组成,即12 * 5 = 60
//精度和纬度为50/50,除以2,即最大长度为30个二进制

$ originPrecision = 30< br>
// latitude
虽然($ latLength < $ OriginPrecision){

//大于中间值,但它是正确的间隔,值为1,否则,如果($ lat > = $ middle =($ MinLat+$ MaxLat)/2){
$ latList = 1,则它为0
;
$ minLat = $ middle< br> }其他{
0;
$ maxLat = $ middle< br> }

$latLength++;
}
//经度
同时($ lngLength < $ OriginPrecision){

如果($ lng & gt= $ middle =($ MinLng+$ MaxLng)/2){
$ LNglist = 1;
$ minLng = $ middle< br> }其他{
0;
$ maxLng = $ middle
}

$ LNglength+;< br>}

var_dump(内爆(",$latList),内爆(",$ lngList));死亡;< br>3。偶数表示经度,奇数表示纬度

通过二进制算法,我们得到了腾讯大厦经纬度的二进制字符串如下:

string(30)" 101011000101000111101101101 "
string(30)" 110101100101001110011010 "

现在需要以偶数表示经度,以奇数表示纬度"你怎么理解这个?起初,我不知道如何操作。后来,经过一系列思考,我可以如下操作:

定位位置

偶数纬度和奇数纬度

。因为我无法用语言表达,所以我剪下了一张操作图。如图表上箭头操作顺序所示,将纬度向右移动一个位置,然后按顺序将其串连起来。

是在php代码中实现的,这似乎更容易理解:

//偶数表示经度,奇数表示纬度
$ stringList =
对于($ I = 0;$i <。30岁;$ I+){
$ string list。= $ lnglist,因此,对应于上表的位置28 25 28 3 6 20 15 31 5 22 25是:wtw366ngz5qt

同样,我们也使用php算法来实现:

//base32映射
$ base32 code = " 0123456789 bcdefghjkmpqrstuvwxyz ";
$ encodeString =
foreach($代码为$ value){
$ EnCodestrading。= $ base32Code代码{ $ value };
}

var _ dump($ EnCodestring);死亡;
这样,我们最终得到的精度问题是,上海腾讯大厦经纬度对应的地理哈希是wtw366ngz5qtgeohash

geohash实际上代表一个矩形块区间,共分为最多12个字符串,即从1到12级。字符数量越多,块间隔越小,定位越准确

我们刚刚用12个级别计算了上海腾讯大厦的地理位置,基本计算位置是毫秒级,可以说非常准确。高于

定位位置

的是与geohash字符串长度相对应的区间精度。我们可以看到,当geohash为12位时,意味着间隔为37毫米,这已经非常准确了。当geohash为6位时,表示为1.2k范围内的矩形位置

,因此,当两个geohash位置的前7位相同时,这意味着它们在附近1.2km的范围内

然后让我们使用腾讯大厦的geohash值分别截取经度的前7位、第6位和第5位数字,看看它在地图上是什么样子:

定位位置

精确度< br>

定位位置

精确度< br>

定位位置

精确度
-5,4.89公里范围

在7,153米范围内。因此,根据上图,随着字符越来越少,精确度越来越小,矩形也越来越大在实际应用中,我们可以动态调整精度来实现更大或更小范围的搜索,这样不仅可以准确定位,还可以隐藏位置的位置信息

geohash

的边界问题因为geohash代表一个块的信息,所以它被认为是在同一块中的两个位置中最接近的,然而,更接近的位置可能正好在另一个间隔中,从而导致失配问题有一个边界问题

让我们看看实际的例子我们想在腾达1.5公里内找到便利店。我们选择精确度为6的geohash公园里有两个a和b。b离我们更近,但是因为A和腾达在一个散列块中,所以发现A是最好的选择。这是边界问题如何解决

-13边界问题

然后如何解决这个边界问题,并给出最新的最佳算法?答案是:计算该位置附近8个方向的geohash。最后,分别计算这些点与自身的距离(因为范围很小,点数也很少,计算量也很小),过滤掉不符合条件的点,就可以了。

定位位置

8 geohash都是通过余弦定理和弧度计算方法来计算两点之间的距离

,最后推导出的公式a是

$ s = acos(cos(rad lat 1)* cos(rad lat 2)* cos(rad ln G1-$ rad ln G2)+sin(rad lat 1)* sin(rad lat 2))* $ r;< br>

目前主要由谷歌的公共距离计算公司使用,推导公式b为:

$ s = 2 * asin(sqrt(sin(($ rad lat 1-$ rad lat 2)/2),2)+cos(rad lat 1)* cos(rad lat 2)* power(sin(($ rad ln 1-$ rad ln G2)/2),2))* $ r;< br>

其中:

$radLat1,$radLat1,$radLat2,$“Radlnt2是弧度

$R是地球半径

在PHP中实现:

函数获取距离(Lat1,$ lng1,$ lat2,$ LNG 2){
//地球半径
$ R = 6378137< br> //将角度转换为福克斯度
$ radlat 1 = deg 2 rad($ lat 1);
$ radlat 2 = deg 2 rad($ lat 2);
$ radln G1 = deg 2 rad($ ln G1);
$ radlng 2 = deg 2 rad($ LNG 2);< br> //结果
$ s = acos(cos(rad lat 1)* cos(rad lat 2)* cos(rad ln G1-$ rad ln G2)+sin(rad lat 1)* sin(rad lat 2))* $ r;< br> //精度< br> $s =四舍五入($ s * 10000)/10000;
往返费用($ s);
}
/*
*查找两个已知纬度和经度之间的距离,单位为米< br> * @param lat1,lat2 latitudes
* @ paramln1,lng2经度
* @返回浮动距离,单位为米
*/
函数get distance tool
{
//地球半径
$ R = 6378137< br>
//deg2rad函数将角度转换为弧度

$ a = $ rad lat 1-$ rad lat 2;
$ b = $ radln G1-$ radln G2;< br>
$s = 2 * asin(sqrt(功率(sin(a/2),2)+cos(rad lat 1)* cos(rad lat 2)*功率(sin(b/2),2)))* $ R;
返回$ s;在redis中实现geohash。redis在3.2.0中添加了与地理相关的命令来支持geohash

redis中的经度和纬度由52位整数编码并放入zset中,其中zset的值元素是键,score是GeoHash的52位整数值当使用redis查询Geo时,其内部对应的操作实际上只是zset(skiplist)的操作坐标附近的其他元素可以通过对zset的分数进行排序来获得,元素的原始坐标

redis可以通过将分数减少到坐标值来获得。处理这些地理位置坐标点的思想是:二维平面坐标点->一维整数编码值-> Zset(score是编码值)-gt;Zrangebyrank(获取具有相似分数的元素),zrangebycore-->;坐标点的分数反解-->;附近点的地理坐标

redis中有6个命令支持地理定位算法。您可以访问redis官方网站查看其使用情况:https://Redis.io/commands # geo

geo add
geo pos
geo dist
geo adius
geo adius by member
geo hash

其他数据

geohash在线转换:http://geohash.co/

转换+地图定位:https://www.movable-type.co.uk/scripts/geohash.html

可以在我们的“腾讯科技”直湖栏目上盖章:

定位位置


256

大家都在看

相关专题