在很多的APP或者移动互联网的应用中,地图经纬度和用户地理位置以及附近的人常常吸引到用户的使用。
如果是全国的用户,这个时候又恰巧需要按照由近到远的分页展示。已知我的经纬度(可能是实时定位获取的),也知道其他用户(可能是商家)的经纬度.
常规思路:取出所有数据的经纬度,进行计算距离,并php数组排序截取分页。但是问题发生了,当数据量很大的时候,就会存在取数据很多,计算两点间的距离也很耗时,这样就会存在性能的瓶颈。会导致cpu和内存负荷过高,甚至服务器宕机等。
下面是优化后的思路:根据我的经纬度加减一定的经纬度,在数据库里面取出来,如果没取到数据就继续扩大范围,直到取到数据。对于这个相应的参数和扩大的范围根据你的需要去设置。我在此处设置为,第一次经纬度绝对值为1,5,10,50,100,200(其实可以不用到200,经度为正负180,纬度为正负90,如果你是国内应用的话,这个更加可以细化,请自己去百度地图上取值。)
下面直接上代码(有些粗糙,欢迎各位进行优化,需要的话可以写个递归)此处使用ThinkPHP框架
//获取附近的人
public function nearbyFriends(){
header('Content-Type:text/html;Charset=UTF-8'); //字符编码
$lat = I("lat");//纬度
$lng = I("lng");//经度
$page = I("page") ? I("page") : 1;
$rows = 20;
$where = " state=1 ";//状态正常的朋友
if($lat && $lng){//有经纬度
$lat1 = $lat + 1; $lat_1 = $lat - 1;
$lng1 = $lng + 1; $lng_1 = $lng - 1;
$where1 = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
$askCnt = M("member_list")->where($where1)->count("id");//统计未读消息数
if($askCnt < 500 ){
$lat1 = $lat + 5; $lat_1 = $lat - 5;
$lng1 = $lng + 5; $lng_1 = $lng - 5;
$where5 = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
$askCnt = M("member_list")->where($where5)->count("id");//统计未读消息数
if($askCnt < 500 ){
$lat1 = $lat + 10; $lat_1 = $lat - 10;
$lng1 = $lng + 10; $lng_1 = $lng - 10;
$where10 = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
$askCnt = M("member_list")->where($where10)->count("id");//统计未读消息数
if($askCnt < 500 ){
$lat1 = $lat + 50; $lat_1 = $lat - 50;
$lng1 = $lng + 50; $lng_1 = $lng - 50;
$where50 = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
$askCnt = M("member_list")->where($where50)->count("id");//统计未读消息数
if($askCnt < 500 ){
$lat1 = $lat + 100; $lat_1 = $lat - 100;
$lng1 = $lng + 100; $lng_1 = $lng - 100;
$where100 = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
$askCnt = M("member_list")->where($where100)->count("id");//统计未读消息数
}
}
}
}
}
if($askCnt){//取到500条数据---根据经纬度
$gap_where = $where . " AND lat>'{$lat_1}' AND lat'{$lng_1}' AND lng
}else{//根据经纬度没取到数据
$gap_where = $where;
}
$offset = floor($page / 25);//每次取500条
$dbPageSize = ($offset+1) * 500;//取500条进行计算排序
$arr = M("member_list")->where($gap_where)->limit("{$offset},{$dbPageSize}")->select();
//echo M("member_list")->_sql();
if($arr){
foreach ($arr as $key => $value) {
if($lat != 0 && $lng != 0 && $value['lat'] && $value['lng']){
$my_map = $lat.','.$lng;
$vip_map = $value['lat'].','.$value['lng'];
$distance = $this->getDistance($my_map,$vip_map);
}else{
$distance = mt_rand(100,2000);//给个随机的距离
}
//排序的arr
$arrSort[$value['id']] = $distance;
$arr[$key]['distance'] = $distance;
if($distance >= 1000){
if($distance < 1000000){//8.88km
$arr[$key]['distance'] = round($distance/1000,2)."km";
}else{//102km
$arr[$key]['distance'] = ceil($distance/1000)."km";
}
}else{//385m
$arr[$key]['distance'] = $distance."m";
}
}
asort($arrSort);
$newArr = array();//所有的
foreach($arrSort as $kk => $vv){
foreach($arr as $kkk =>$vvv){
if($kk == $vvv['id']){
$newArr[] = $vvv;
}
}
}
$start = ($page-1)*$rows;//计算每次分页的开始位置
$total = count($newArr); //总的记录数
$countPage = ceil($total/$rows); //计算总页面数
$pageData = array();
$pageData = array_slice($newArr,$start,$rows);// 最后一个值为true 保留原来键
$data_list["firends"] = $pageData;
$this->result_print(1,"获取附近的人成功!",$data_list);
}else{
$this->result_print(0,"未找到附近的人!");
}
}
附上计算两点间距离的方法
//求两者经纬度之间的距离-- my_map 我的经纬度---map 商家经纬度
public function getDistanceNew($my_map,$map){
if(!strstr($my_map,',')){ return '未知'; }//找到分号再进行
if(!strstr($map,',')){ return '未知'; }//找到分号再进行
$myMap = explode(',',$my_map);
$mapArr = explode(',',$map);
$lat1 = $myMap['1'];
$lng1 = $myMap['0'];
$lat2 = $mapArr['1'];
$lng2 = $mapArr['0'];
//将角度转为狐度
$radLat1 = deg2rad($lat1);//deg2rad()函数将角度转换为弧度
$radLat2 = deg2rad($lat2);
$radLng1 = deg2rad($lng1);
$radLng2 = deg2rad($lng2);
$a = $radLat1 - $radLat2;
$b = $radLng1 - $radLng2;
$s = 2*asin(sqrt(pow(sin($a/2),2)+cos($radLat1)*cos($radLat2)*pow(sin($b/2),2)))*6378.137*1000;
return round($s);
}
你可以根据你的用户数据进行颗粒度细化。根据你的需求进行优化你的数据吧。
有兴趣的人会redis的也可以研究研究 http://redisdoc.com/geo/georadius.html
很多人就像山川,像河流。山川不曾回应,河流一去不返,却依然在你生命里,波澜壮阔。