apicloud apicloud

注册
查看: 12712|回复: 28

[新手教程] 手把手教你如何实现查看附近的人

 

主题:
53
帖子:
333
云币:
552

APICloud粉丝APICloud毕业勋章一周年

[新手教程] 手把手教你如何实现查看附近的人

 
12712 28 | 发表于 2015-4-29 16:02:28 |阅读模式 | |
本帖最后由 流浪男 于 2015-4-29 16:04 编辑

今天分享的教程是教你如何实现附近的人或者其他内容。服务器端用的php。
使用前提请确认你的用户数据表中是否有存储用户的最新坐标和更新时间,最后单独建一张表来存储用户的最新坐标和更新时间。

在获取附近的人之前首先要获取自己的坐标。可以使用baiduLocation来获取当前用户的坐标,然后用当前坐标请求服务器返回按照距离排序的用户数据。


apiready = function() {
    var baiduLocation = api.require('baiduLocation');
    baiduLocation.startLocation({
        accuracy: '100m',
        filter:1,
        autoStop: true
    }, function(ret, err){
        var sta = ret.status;
        var lat = ret.latitude;
        var lon = ret.longitude;
        if(sta){
            //成功获取
        }else{
            //获取失败
        }
    });
};


//获取位置成功后,开发向服务器发送请求

api.ajax({
    url: 请求地址,
    method: 'post',
    timeout: 30,
    dataType: 'json',
    returnAll:false,
    data:{
        values: {lat: lat,lon:lon}
    }
},function(ret,err){
    if (ret) {
        var urlJson = JSON.stringify(ret);
        api.alert({msg: urlJson});
    }else {
        api.alert({
            msg'错误码:'+err.code+';错误信息:'+err.msg+'网络状态码:'+err.statusCode)
        });
    };
});



其实在APP端代码非常简单,主要就是获取坐标然后发送到服务器,然后服务器根据传过来的坐标来计算距离,按照距离排序返回数据。那么重点就是服务器端如何实现了

服务器端就以php为例来讲一下,
首先获取有坐标用户的数据,这个就是foreach一下了,然后根据传过来的坐标计算距离,下面是foreach里面的一段代码
假设 用户数据为  $data;

//foreach之前先组装下post过来的坐标
$lat = $_POST['lat'];
$lon = $_POST['lon'];
$myLocation = $lon.','.$lat;

foreach($data as $key=>$v){
  //E:对方用户坐标为: 104.077638,30.673573
$v['position'] = "104.077638,30.673573";
  $newData[$key]['distance] = distanceBetween($myLocation,$v['position']);
  .......
  //其他用户数据
}

然后再foreach一下新数组根据距离来排序
foreach ($newData as $key => $r) {
    $distance[] = $r['distance'];
}

array_multisort($distance, SORT_ASC, $newData);
输出JSON数组
echo json_encode($newData);
注:上面foreach里面有个自定义函数distanceBetween();这个是用来计算两个坐标的距离的,代码如下:
/**
* 计算两个坐标之间的距离(米)
* @param float $fP1Lat 起点(纬度)
* @param float $fP1Lon 起点(经度)
* @param float $fP2Lat 终点(纬度)
* @param float $fP2Lon 终点(经度)
* @return int
*/
function distanceBetween($mylonlat, $findlonlat){
        $mylonlat = explode(',', $mylonlat);
        $findlonlat = explode(',', $findlonlat);
        list($lng1,$lat1) = $mylonlat;
        list($lng2,$lat2) = $findlonlat;
        $EARTH_RADIUS=6378.137;
        $PI=3.1415926;
        $radLat1 = $lat1 * $PI / 180.0;
        $radLat2 = $lat2 * $PI / 180.0;
        $a = $radLat1 - $radLat2;
        $b = ($lng1 * $PI / 180.0) - ($lng2 * $PI / 180.0);
        $s = 2 * asin(sqrt(pow(sin($a/2),2) + cos($radLat1) * cos($radLat2) * pow(sin($b/2),2)));
        $s = $s * $EARTH_RADIUS;
        $s = round($s * 1000);
        if ($len_type > 1) {
                $s /= 1000;
        }
        $distance = round($s/1000,2);
        return $distance;
}



1

查看全部评分

实习司机

UID:23575

主题:
21
帖子:
195
云币:
768

社会摇

发表于 2015-7-30 10:28:08 |
本帖最后由 xnceo 于 2015-7-30 10:30 编辑

把用户的坐标记录到内存中,如memcached , redis 中,加快查询速度,如没有这两个,那就经纬分开记录到数据库字段中,并分别建立索引,查询时分两次查询,不要写成一条where条件,否则索起不到多大效果。按经范围搜索出大概的数据再计算。
当然位置的计算也可以分散到用户的设备上,不过这样要改一改,将上面的PHP改成javascript版的计算距离,我给个我用的吧:

  1. <script type="text/javascript">
  2. /*
  3.   获取坐标地址:http://api.map.baidu.com/lbsapi/getpoint/index.html
  4.   计算地图两点距离,返回的数组由距离(d[0])和单位(d[1])组成
  5.   dis(起点经,纬,终点经,纬)
  6. */
  7. function dis(sLon, sLat, eLon, eLat){
  8.         if( (sLon==0 && sLat==0) || (eLon==0 && eLat==0) || sLon == undefined || sLat == undefined || eLon == undefined || eLat == undefined ){
  9.                 return new Array("unknow");
  10.         }
  11.         var arr = new Array(2);
  12.         
  13.         var radLat1 = sLat * Math.PI/180.0;
  14.         var radLat2 = eLat * Math.PI/180.0;
  15.         var a = radLat1 - radLat2;
  16.         var b = (sLon * Math.PI/180.0) - (eLon * Math.PI/180.0);

  17.         var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b/2),2)));
  18.         s = s * 6378.14;
  19.         s = Math.round(s*1000)/1000;
  20.         if(s<1){
  21.                 s  *= 1000;
  22.                 arr[1] = 'm';
  23.         }else{
  24.                 s = Math.round(s*10)/10;
  25.                 arr[1] = 'km';
  26.         }
  27.         arr[0] = s;
  28.         return arr;
  29. }

  30. //示例 //
  31. var d = dis(0,0, 120.152559,30.258524);
  32. document.write( d.join("") +"<br>");
  33. //白堤
  34. var d = dis(120.158452,30.264621, 120.152559,30.258524);
  35. document.write( d.join("") +"<br>");
  36. //堤 -> 校
  37. var d = dis(120.158452,30.264621, 120.172704,30.296707);
  38. document.write( d.join("") +"<br>");
  39. //堤 -> 金
  40. var d = dis(120.158452,30.264621, 119.745303,29.12065);
  41. document.write( d.join("") +"<br>");
  42. </script>
复制代码

驾校小白

UID:103710

主题:
6
帖子:
16
云币:
82
发表于 2015-6-23 11:21:53 |
楼主的帖子很有帮助,对于我这样的初学者,从中受益很多,感谢楼主。

最近,我有一个想法,没有测试过,但我觉得应该也能实现查找附近人员的功能。首先有两步准备工作:第一步是标记在线可以接受信息的人员(在使用这个应用程序,或者设置为接受离线消息等等),这一步的目标是缩小并出可以被查找交流的人员(从所有注册用户中,选出部分备选用户),用布尔变量标记一下就行;第二步是更新这些人员的地理信息内容,也就是坐标(每个用户在打开应用程序的时候,跟服务器通信,更新最新的地理坐标信息;离线接收信息的用户,定期跟服务器通信,更新自己的坐标),存储在数据库里面的GeoPoint字段下面即可。

做好以上两步准备工作,那么,查找附近的人员就转化为一个数据库查询问题,用数据云API提供查询功能的near操作符,向服务器端发送自己的GeoPoint,给定具体的设定条件(比如3km以内的人员等等),然后用near方法发送一条请求,就可以得到满足要求(在线可接受信息,距离3km以内)的人员的信息。

以下是文档中相关的内容,希望对大家有帮助。

near

查询的是GeoPoint列,格式为:维度,经度

REST

GET /mcm/api/locations?filter[where][geo][near]=-28.1,153.536&filter[limit]=3

Stringified

GET /mcm/api//locations?filter={"where:{"geo":{"near":"-28.1,153.536"}},"limit":3}

maxDistance

对地理位置做范围限制,必须配合near使用 REST

GET /mcm/api/locations?filter[where][geo][near]=-28.1,153.536&filter[where][geo][maxDistance]=1000&filter[limit]=3

Stringified

GET /mcm/api//locations?filter={"where:{"geo":{"near":"-28.1,153.536","maxDistance":1000}},"limit":3}

type

对地理位置做范围单位限制,必须配合near使用,默认为meters 类型

miles(英里)
radians(弧度)
kilometers(公里)
meters(米)
feet(英尺)
degrees(角度)

REST

GET /mcm/api/locations?filter[where][geo][near]=-28.1,153.536&filter[where][geo][maxDistance]=1000&filter[where][geo][type]=kilometers&filter[limit]=3

Stringified

GET /mcm/api//locations?filter={"where:{"geo":{"near":"-28.1,153.536","maxDistance":1000,"type":"kilometers"}},"limit":3}

更多内容可以进一步察看文档。

以上只是我的一些想法,如果有不妥之处,请大家多多指正,非常感谢!

主题:
56
帖子:
298
云币:
1159
发表于 2016-3-11 00:39:28 |

主题:
1077
帖子:
4833
云币:
2147218643

圣诞节捣蛋鬼APICloud大会专属勋章足球狗儿童节快乐

发表于 2015-4-29 17:06:13 |
又来分享咯。太给力了

主题:
24
帖子:
104
云币:
292
发表于 2015-4-29 16:05:22 |
赞一个,感忽而很有用的样子!

实习司机

UID:20052

主题:
12
帖子:
68
云币:
14

你吃屎!女朋友

发表于 2015-4-29 16:06:57 |
强悍~~~~~~~~~~~~~

主题:
0
帖子:
6
云币:
11
发表于 2015-4-29 20:01:20 |
不错不错 谢谢分享
有意义 u1e.cn

主题:
39
帖子:
125
云币:
1099
发表于 2015-6-15 06:47:45 |
正是下步要用的

新手上路

UID:54932

主题:
12
帖子:
117
云币:
384
发表于 2015-6-16 14:14:55 |
这样用循环的方式,性能方面如何提高呢?????如果用户量大,如何可以实时找到附件的人呢?

新手上路

UID:100623

主题:
1
帖子:
47
云币:
60

APICloud粉丝

发表于 2015-6-16 15:38:47 |
点个赞,谢谢分享

驾校小白

UID:119336

主题:
0
帖子:
5
云币:
27
发表于 2015-7-29 23:43:04 |
,正好用到,感谢哈
123下一页
您需要登录后才可以回帖 登录 | 立即注册

快速回复 返回顶部 返回列表