最近在做一款移动端棋牌游戏,为了进一步提高用户体验、拉近玩家的距离,我们决定在游戏中加入好友功能,而对于体验好友功能的玩家来说,要是玩牌的时候可以看看附近都有谁在玩牌,跟他们交流交流玩牌心得什么的无疑是个不错的想法。而要实现查看附近的人就需要提提LBS(Location Based Service),他的意思就是基于位置的服务,就是通过移动终端获取到许多用户或者物体的经纬度坐标,通过这些位置信息所提供的服务。

好了,扯了这么多,我们来看看如何实现查看附近人的功能的:

首先要具备下面这些环境:

    1. php+MySQL(MySQL不是必须,本文中用的是redis来存储用户的信息)
    2. redis(本文用的是redis,当然你也可以用MySQL)
    3. geohash.class.php类(这个类是用来处理经纬度坐标的一些基本函数,当然这些东西完全可以自己去写,如果时间充裕的话)

好了,等这些环境都具备了之后,我来讲讲这个实现过程:

  • 首先介绍下GeoHash思想

   第一步.  编码

这个功能应用到了一个很好的算法GeoHash,也许有同学听过这个功能,没错GeoHash就是通过一个巧妙的算法(不由得惊叹前辈们真牛!)把经纬度转化为字符串,这样有什么好处呢,显而易见,将二维的数据转化为了一维,这样一来存储就方便了,搜索效率也会高很多,那么现在问题来了,GeoHash算法是如何把经纬度坐标转化为字符串的?

将经纬度编码为字符串的过程可以分为以下3个步骤:

首先就是编码,对于经纬度的编码通过折半比较法,当大于中值时该位编码为1(小于时编码为0),下次新的区间为中值到最大值(或者最小值到中值),这样一直比较下去,直到到达要求的精度,精度和纬度的方法是一样的,只不过一个原始区间是(-90,90),一个是(-180,180),光说不好理解,下面我们看看一个简单的例子:

对经度32.165进行编码:                                                                                                 对纬度89.156进行编码:

编码

min

mid

max

1

-90

0

90

0

0

45

90

1

0

22.5

45

0

22.5

033.75

45

1

22.5

28.125

33.75

1

28.125

30.9375

33.75

0

30.9375

32.34375

33.75

1

30.9375

31.640625

32.34375

1

31.640625

31.9921875

32.34375

0

31.9921875

32.16796875

32.34375

编码

min

mid

max

1

-180

0

180

0

0

90

180

1

0

45

90

1

45

67.5

90

1

67.5

78.75

90

1

78.75

84.375

90

1

84.375

87.1875

90

1

87.1875

88.59375

90

0

88.59375

89.296875

90

1

88.59375

88.9453125

89.296875

这样便可将(89.156,32.165) =>  (10101 10110,10111 11101)

这个时候就到了第二步骤——组码了,顾名思义,将第一步产生的编码组合起来为下一步产生字符串做准备,组码的方式是偶数位放置经度,奇数位放纬度(为什么要这么做呢,我猜可能是谷歌为了大家统一规范,仅此而已,其实奇偶数位互换也可以的),对于上面的经纬度编码后再组码如下:

经度:10101 11101

纬度:10111 11101

位置编码:11001 11011 11111 10011

图 3

对于上面的位置编码,为什么要这么编码呢,为什么要奇数位放纬度,偶数为方经度呢,我们看看下面这张图,用这张图模拟地图的经纬度,A点(-180,90),B点(180,90),C点(-180,-90),D点(180,-90);

A

B

 

010101

010111

011101

011111

110101

110111

111101

111111

010100

010110

011100

011110

110100

110110

111100

111110

010001

010011

011001

011011

110001

110011

111001

111011

010000

010010

011000

011010

110000

110010

111000

111010

000101

000111

001101

001111

100101

100111

101101

101111

000100

000110

001100

001110

100100

100110

101100

101110

000001

000011

001001

001011

100001

100011

101001

101011

000000

000010

001000

001010

100000

100010

101000

101010

 

C

D

图 4

如图4所示,这样就可以将地图(经度-180~180,纬度-90~90)分为很多很多多的小块,每一个小块都有唯一的二进制编码,当位数达到一定的长度时就可以表示很小的一块区域,这不就可以根据二进制编码定位一个唯一的位置了吗,对于划分的进一步理解可看下面的图。

图 5
      如图5所示,左边是是对纬度(-180,180)的划分,可以看出通过划分可以确定(22.4,45)这一纬度区间的编码为1001,当然了位数越多精度越高,同理对经度进行划分,可以确定(-78.75,-67.5)这一经度范围的编码为0001,可以想象,当左右两张图合在一起时就可以确定一个唯一的矩形区域,当该区域足够小的时候就可一看做一个点。
 
      第二步.  组码
      从图3可以看出我们对经纬度编码后可得二进制字符串11001 11011 11111 10011
      最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11001 11011 11111 10011转成十进制,对应着25、27、32、19,十进制对应的编码就是tvzm。同理,将编码转换成经纬度的解码算法与之相反,具体不再赘述。至此,我们的对geohash有了个大致的了解。

图 6

  • 如何具体的应用到程序中

首先思考一下查看附近的人的流程:

    1. 用户点击查看附近的人按钮,首先获取到该用户的选位置信息(经纬度),传给服务器。
    2. 服务器收到数据之后对该用户的位置信息进行geohash计算,获得该用户的位置hash字符串。
    3. 对该用户的位置信息hash串进行缓存(缓存时间长短根据具体情况而定)。
    4. 根据该hash串选出附近的人。
    5. 对hash进行解码,计算出附近用户的位置,返回给用户。

首先看看geohash.class.php这个公共类库里面的基本方法:

 

[public]Geohash()       初始化hash映射表

Geohash

[public]encode($lat,$long)       对经纬度进行编码

 

[public]decode()       对hash进行解码

图 7

如图7所示,显而易见这个类库里面有3个函数,第一个用来初始化hash映射表,其实就是把0123456789bcdefghjkmnpqrstuvwxyz字符串中的每个字符和它对应的二进制编码对应起来(左边补零至5位)。encode()是用来生成hash的,decode是用来解码hash得到hash对应的经纬度的。

        下面我们看个例子,现在假设有图8中的几个用户查看附近的人:

mid

坐标

(42.61233,-5.61234)

(-20.25689, 50.56897)

(10.11233, 57.21234)

(49.26343, -123.26895)

(0.00534, -179.56732)

(-30.55555, 0.28958)

(5.00001, -140.63422)

(42.61234, -5.61234)

(5.00001, -140.63422)

图 8

图8的数据发送到服务器经过geohash计算得出下面的hash表:

 

mid

坐标

100

ezs42m34yfp_100

mh7uy8r5n6j_101

t3b9tbuu84u_102

c2b26bnk32b_103

80021bgp45m_104

k484ntdc58w_105

8bgury1r1jm_106

ezs42m34ygz_107

8bgury1r1jm_108

图 9

计算出这些hash值,将hash值存入redis中,存入redis中之后,那么问题来了,如何去获取一个用户附近的用户呢?当redis数据库中有了一些用户的记录之后,来一个用户,我们先对其进行编码,然后根据该用户的位置hash从redis中选出该用户附近的hash,选取附近的hash这一步很简单,对于redis只需这么做:

<?php
$mid = 2014;
$level = 7; //获取的精度等级,数字越大,附近这个范围越小
$redis = Redis::init(); //假设这样获取到redis实例
$mykey = '8gur95yjmz'; //假设我的hash为这个
$redis->setex($mykey.'_'.$mid,$_SERVER['REQUEST_TIME'],86400); //这里设置缓存1天,具体情况具体对待
$search = substr($makey,0,$level);
$nearbys = $redis->keys("{$search}*");
?>

程序 1

上面的几句代码就可以选出我附近的人的hash,当然,其中的level来设置精度的,这个数字越大,附近的人范围越小,具体参考图10中的值,这个表中的值是从我的导师李伟(weickly)那里获取到的。要注意,这个地方搜索完之后要排除自己。还有一点要注意,就是在缓存时键名的最后一定要加上_{$mid},这样做可以避免多个用户在同一位置是互相覆盖的情况(就像图9中mid为106和108的用户),放在最后是为了不影响搜索。

1

2500000m

2

630000m

3

78000m

4

20000m

5

2400m

6

610m

7

76m

8

19m

9

2m

图 10
        例如mid为109,经纬度为(42.61236, -5.61234)的用户,当他点击获取附近的人按钮式,我获取到他的经纬度并计算出他的hash,$mykey='ezs42m34yzx',然后通过程序段1可以获取到他附近的hash:

100

ezs42m34yfp_100

(42.61233,-5.61234)

3.3m

107

ezs42m34ygz_107

(42.61234, -5.61234)

2.2m

109

ezs42m34yzx_109

(42.61236, -5.61234)

0m

图 11
        图11中获取到了用户109附近的用户hashs,获取到hash值还并没有完成,首先排除掉自己109那条记录,然后通过Geohash类中的decode将hash解码为经纬度,通过每个用户的经纬度计算出和109用户的距离,然后按距离等级返回,比如说小于100,小于200……
        至此,获取附近的人就完成了,当然了具体实践的时候还要随机应变具体情况具体对待,我写这篇文章只是想起到抛砖引玉的效果,本文中可能会存在很多不足,还望斧正。
 
  本文版权归作者(luluyrt@163.com)和博客园共有,未经作者本人同意禁止任何形式的转载,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。 
 

lbs(查看附近的人),看看社交软件如何实现查看附近的人的更多相关文章

  1. IT软件外包行业深入分析:现状、前途、趋势、待遇 什么是软件外包 外包公司是怎么工作的 软件外包公司的面试 软件外包公司需要什么样的人

    目录: [0] - 为什么要对大学生谈软件外包? [1] - 什么是软件外包? [2] - 软件为什么要外包? [3] - 为什么要承接软件外包 [4] - 做软件外包有前途吗? [5] - 外包公司 ...

  2. 快播王欣发布匿名IM社交软件“马桶MT”

    2019年1月14日,快播王欣推出了一款匿名IM社交软件——马桶MT,它的灵感像是来自于美国的匿名分享应用Secret(已关闭). 原快播创始人王欣近日在微博预告了其新公司云歌人工智能推出一款全新社交 ...

  3. C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享

    原文:C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享 曾经我做office,不想依赖别人dll,就使用了 Type.GetTypeFromProgID 可以根 ...

  4. 三行代码接入,社交软件打字时底下弹出的表情布局,自定义ViewPager+页面点标+各种功能的android小框架。

    (转载请声明出处:http://www.cnblogs.com/linguanh/) 前言: 接上次分享的 ListView 动态加载类,入口:http://www.cnblogs.com/lingu ...

  5. 第一章-第六题(帮人抢票,帮人选课这些软件是否合法 你怎么看?)--By梁旭晖

    我觉得这些软件是合法的,符合道德规范的. 计算机当初设计的初衷就是简化甚至替代人类的工作.而软件作为计算机硬件的驱动着,其设计就是体现这些原则. 现在互联网上的订票,选课类型的网站还是有很多的,比如: ...

  6. C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享

    原文:C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享 1 : C# Assembly.GetEntryAssembly().GetName().Version. ...

  7. Threejs 的场景查看 - 几个交互事件库助你方便查看场景

    Threejs 的场景查看 - 几个交互事件库助你方便查看场景 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&q ...

  8. jQuery 图片查看插件 Magnify 开发简介(仿 Windows 照片查看器)

    前言 因为一些特殊的业务需求,经过一个多月的蛰伏及思考,我开发了这款 jQuery 图片查看器插件 Magnify,它实现了 Windows 照片查看器的所有功能,比如模态窗的拖拽.调整大小.最大化, ...

  9. Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程

    2.Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程 2014-12-07 23:39 2623人阅读 评论(0) ...

随机推荐

  1. 基于Spring的简易SSO设计

    通常稍微规模大一些的企业,内部已经有很多的应用系统,多个系统整合首先要解决的便是“统一登录(SSO)”问题,之前写过一篇 利用Membership实现SSO(单点登录) ,java环境下已经有一些开源 ...

  2. SQL基础之GROUPING

    1.grouping sets 记得前几天第一次接触grouping sets时,笔者的感觉是一脸懵逼. 后来一不小心看到msdn上对grouping sets的说明,顿时豁然开朗,其实groupin ...

  3. ADO.NET 学习笔记 入门教程

    本文转载自:http://www.youarebug.com/forum.php?mod=viewthread&tid=57&page=1&extra=#pid63 这是本人在 ...

  4. Nodejs爬虫进阶=>异步并发控制

    之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一 ...

  5. swagger editor使用

    swagger editor使用 swagger是一套开源的API设计工具,包括Swagger UI,Swagger Editor等. Swagger Editor 其中Swagger Editor是 ...

  6. 沃罗诺伊图(Voronoi Diagram,也称作Dirichlet tessellation,狄利克雷镶嵌)

    沃罗诺伊图(Voronoi Diagram,也称作Dirichlet tessellation,狄利克雷镶嵌)是由俄国数学家格奥尔吉·沃罗诺伊建立的空间分割算法.灵感来源于笛卡尔用凸域分割空间的思想. ...

  7. We Know What @You #Tag: Does the Dual Role Affect Hashtag Adoption-20160520

    分析类的论文 1.Information publication:www2012 author: Mei qiao zhu 2.What 微博中的hashtag既可以表示谈论的内容,又可以代表一个群体 ...

  8. MVC认知路【点点滴滴支离破碎】【五】----form表单上传单个文件

    //个人理解:前台一个form加input[type='file'],在加一个submit的按钮 主要设置form的action,method,enctype='multipart/form-data ...

  9. 天气预报API获取

    1.citycode: http://mobile.weather.com.cn/js/citylist.xml http://files.cnblogs.com/files/ys-wuhan/cit ...

  10. swift项目实战--微博的未登录界面的实现,和监听未登录界面两个按钮的两种实现方法

    1.未登录界面的实现 微博项目中,用户不登录的话,显示的是未登录的界面.项目中TabBarVC的子控制器都是tableViewVC,所以抽取了父类,让父类判断用户是否登录,决定显示什么样的界面.loa ...