lbs(查看附近的人),看看社交软件如何实现查看附近的人
最近在做一款移动端棋牌游戏,为了进一步提高用户体验、拉近玩家的距离,我们决定在游戏中加入好友功能,而对于体验好友功能的玩家来说,要是玩牌的时候可以看看附近都有谁在玩牌,跟他们交流交流玩牌心得什么的无疑是个不错的想法。而要实现查看附近的人就需要提提LBS(Location Based Service),他的意思就是基于位置的服务,就是通过移动终端获取到许多用户或者物体的经纬度坐标,通过这些位置信息所提供的服务。
好了,扯了这么多,我们来看看如何实现查看附近人的功能的:
首先要具备下面这些环境:
- php+MySQL(MySQL不是必须,本文中用的是redis来存储用户的信息)
- redis(本文用的是redis,当然你也可以用MySQL)
- 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 |
对于上面的位置编码,为什么要这么编码呢,为什么要奇数位放纬度,偶数为方经度呢,我们看看下面这张图,用这张图模拟地图的经纬度,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所示,这样就可以将地图(经度-180~180,纬度-90~90)分为很多很多多的小块,每一个小块都有唯一的二进制编码,当位数达到一定的长度时就可以表示很小的一块区域,这不就可以根据二进制编码定位一个唯一的位置了吗,对于划分的进一步理解可看下面的图。


图 6
- 如何具体的应用到程序中
首先思考一下查看附近的人的流程:
- 用户点击查看附近的人按钮,首先获取到该用户的选位置信息(经纬度),传给服务器。
- 服务器收到数据之后对该用户的位置信息进行geohash计算,获得该用户的位置hash字符串。
- 对该用户的位置信息hash串进行缓存(缓存时间长短根据具体情况而定)。
- 根据该hash串选出附近的人。
- 对hash进行解码,计算出附近用户的位置,返回给用户。
首先看看geohash.class.php这个公共类库里面的基本方法:
|
[public]Geohash() 初始化hash映射表 |
|
|
Geohash |
[public]encode($lat,$long) 对经纬度进行编码 |
|
[public]decode() 对hash进行解码 |
如图7所示,显而易见这个类库里面有3个函数,第一个用来初始化hash映射表,其实就是把0123456789bcdefghjkmnpqrstuvwxyz字符串中的每个字符和它对应的二进制编码对应起来(左边补零至5位)。encode()是用来生成hash的,decode是用来解码hash得到hash对应的经纬度的。
|
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的数据发送到服务器经过geohash计算得出下面的hash表:
|
mid |
坐标 |
|
100 |
ezs42m34yfp_100 |
|
mh7uy8r5n6j_101 |
|
|
t3b9tbuu84u_102 |
|
|
c2b26bnk32b_103 |
|
|
80021bgp45m_104 |
|
|
k484ntdc58w_105 |
|
|
8bgury1r1jm_106 |
|
|
ezs42m34ygz_107 |
|
|
8bgury1r1jm_108 |
计算出这些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 |
|
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 |
lbs(查看附近的人),看看社交软件如何实现查看附近的人的更多相关文章
- IT软件外包行业深入分析:现状、前途、趋势、待遇 什么是软件外包 外包公司是怎么工作的 软件外包公司的面试 软件外包公司需要什么样的人
目录: [0] - 为什么要对大学生谈软件外包? [1] - 什么是软件外包? [2] - 软件为什么要外包? [3] - 为什么要承接软件外包 [4] - 做软件外包有前途吗? [5] - 外包公司 ...
- 快播王欣发布匿名IM社交软件“马桶MT”
2019年1月14日,快播王欣推出了一款匿名IM社交软件——马桶MT,它的灵感像是来自于美国的匿名分享应用Secret(已关闭). 原快播创始人王欣近日在微博预告了其新公司云歌人工智能推出一款全新社交 ...
- C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享
原文:C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享 曾经我做office,不想依赖别人dll,就使用了 Type.GetTypeFromProgID 可以根 ...
- 三行代码接入,社交软件打字时底下弹出的表情布局,自定义ViewPager+页面点标+各种功能的android小框架。
(转载请声明出处:http://www.cnblogs.com/linguanh/) 前言: 接上次分享的 ListView 动态加载类,入口:http://www.cnblogs.com/lingu ...
- 第一章-第六题(帮人抢票,帮人选课这些软件是否合法 你怎么看?)--By梁旭晖
我觉得这些软件是合法的,符合道德规范的. 计算机当初设计的初衷就是简化甚至替代人类的工作.而软件作为计算机硬件的驱动着,其设计就是体现这些原则. 现在互联网上的订票,选课类型的网站还是有很多的,比如: ...
- C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享
原文:C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享 1 : C# Assembly.GetEntryAssembly().GetName().Version. ...
- Threejs 的场景查看 - 几个交互事件库助你方便查看场景
Threejs 的场景查看 - 几个交互事件库助你方便查看场景 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&q ...
- jQuery 图片查看插件 Magnify 开发简介(仿 Windows 照片查看器)
前言 因为一些特殊的业务需求,经过一个多月的蛰伏及思考,我开发了这款 jQuery 图片查看器插件 Magnify,它实现了 Windows 照片查看器的所有功能,比如模态窗的拖拽.调整大小.最大化, ...
- Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程
2.Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程 2014-12-07 23:39 2623人阅读 评论(0) ...
随机推荐
- Vue系列:通过vue-router如何传递参数
使用vue-router 来实现webapp的页面跳转,有时候需要传递参数,做法如下: 参考文献:http://router.vuejs.org/en/named.html 主要有以下几个步骤: ( ...
- Eclipse 反编译插件JadClipse安装
下载jadClipse地址: 链接: http://pan.baidu.com/s/1kTN4TPd 提取码: 3fvd 将net.sf.jadclipse_3.3.0.jar拷贝到eclipse的 ...
- Python2.4-原理之函数
此节来自于<Python学习手册第四版>第四部分 一.函数基础 函数的作用在每个编程语言中都是大同小异的,,这个表是函数的相关语句和表达式. 1.编写函数,a.def是可执行代码,pyth ...
- Theano2.1.8-基础知识之装载和保存
来自:http://deeplearning.net/software/theano/tutorial/loading_and_saving.html loading and saving Pytho ...
- 我的微型工作流引擎-功能解析及API设计
一.前言 上一篇我给大家介绍了我的工作流的模型和基本的设计,这篇我想详细说明下我这款工作流的功能及使用示例.这款工作流主要是面向开发者设计的,为了先让大家有个全局的认识,局部功能的设计实现就不细说了, ...
- retrofit2中ssl的Trust anchor for certification path not found问题
在retrofit2中使用ssl,刚刚接触,很可能会出现如下错误. java.security.cert.CertPathValidatorException: Trust anchor for ce ...
- Scala函数式编程进阶
package com.dtspark.scala.basics /** * 函数式编程进阶: * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: * 2, 函数更长用的方式 ...
- 一次神奇的WCF的404错误解决
现象:浏览器中可以访问元数据,但是运行的时候却报404的异常,说目标地址找不到. 折腾了一下午. 引用服务后config中的client的address是这样的http://host/aspx/Ser ...
- [Google Guava]学习--缓存cache
适用性 缓存在很多情况下非常实用.例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. Guava Cache与ConcurrentMap很相似,但也不完全 ...
- Linux14.04安装Mysql Linux公社
今天在Ubuntu 14.04下安装MySQL,本来是去官网下载安装包来安装的,可是安装之后却不能用,估计是要配置吧,在网上搜了很多的资料,结果还是失败.所以只好在软件源中安装,这样就省去很多不必要的 ...