目前的工作是需要对用户的一些数据进行分析,每个用户都有若干条记录,每条记录中有用户的一个位置,是用经度和纬度表示的。
还有一个给定的数据库,存储的是一些已知地点以及他们的经纬度,内有43W多条的数据。
现在需要拿用户的经纬度和已知地点进行距离匹配,如果它们之间的距离小于一定的数据,比如说500米,就认为用户是在这个地点。
MYSQL本身是支持空间索引的,但是在5.x的版本中,取消了对Distance()和Related()的支持,无法使用空间的距离函数去直接去查询距离在一定范围内的点。所以,我首先想到的是,对每条记录,去进行遍历,跟数据库中的每一个点进行距离计算,当距离小于500米时,认为匹配。这样做确实能够得到结果,但是效率极其低下,因为每条记录都要去循环匹配40W条数据,其消耗的时间可想而知。经过记录,发现每条记录处理的时间消耗达到1700ms,针对每天上亿的数据量,这样一个处理速度,让人情何以堪啊。。。
我自己也有个想法,就是找到每条记录所在点的经纬度周围的一个大概范围,比方说正方形的四个点,然后使用mysql的空间计算,使用MBR去得出点在这个矩形内的已知记录,然后进行匹配。可惜,自己没想出能计算到四个点经纬度的方法。

意外的,查询到了一个关于这个计算附近地点搜索初探,里面使用python实现了这个想法。
所以参考了一下原文中的算法,使用PHP进行了实现。
实现原理也是很相似的,先算出该点周围的矩形的四个点,然后使用经纬度去直接匹配数据库中的记录。

红色部分为要求的搜索范围,绿色部分我们能间接得到的结果范围

参考wiki百科上的一些球面计算公式:

Great-circle distance
Haversine formula

假设已知点的经纬度分别为$lng, $lat
先实现经度范围的查询,
在haversin公式中令φ1 = φ2,可得:

用PHP进行计算,就是:

Example

 代码如下 复制代码

//$lat 已知点的纬度
$dlng =  2 * asin(sin($distance / (2 * EARTH_RADIUS)) / cos(deg2rad($lat)));
$dlng = rad2deg($dlng);//转换弧度

然后是纬度范围的查询,

在haversin公式中令 Δλ = 0,可得

在PHP中进行计算,就是:

Example

 代码如下 复制代码

$dlat = $distance/EARTH_RADIUS;//EARTH_RADIUS地球半径
$dlat = rad2deg($dlat);//转换弧度

最后,就可以得出四个点的坐标:

left-top : (lat + dlat, lng – dlng)
right-top : (lat + dlat, lng + dlng)
left-bottom : (lat – dlat, lng – dlng)
right-bottom: (lat – dlat, lng + dlng)

我把以上方法写成了一个函数,综合起来就是:

Example

 代码如下 复制代码

define(EARTH_RADIUS,6378.137);//地球半径,平均半径为6378.137km

/**
 *计算某个经纬度的周围某段距离的正方形的四个点
 *
 *@param lng float 经度
 *@param lat float 纬度
 *@param distance float 该点所在圆的半径,该圆与此正方形内切,默认值为0.5千米
 *@return array 正方形的四个点的经纬度坐标
 */
 function returnSquarePoint($lng, $lat,$distance = 0.5){
 
    $dlng =  2 * asin(sin($distance / (2 * EARTH_RADIUS)) / cos(deg2rad($lat)));
    $dlng = rad2deg($dlng);
     
    $dlat = $distance/EARTH_RADIUS;
    $dlat = rad2deg($dlat);
     
    return array(
                'left-top'=>array('lat'=>$lat + $dlat,'lng'=>$lng-$dlng),
                'right-top'=>array('lat'=>$lat + $dlat, 'lng'=>$lng + $dlng),
                'left-bottom'=>array('lat'=>$lat - $dlat, 'lng'=>$lng - $dlng),
                'right-bottom'=>array('lat'=>$lat - $dlat, 'lng'=>$lng + $dlng)
                );
 }
//使用此函数计算得到结果后,带入sql查询。

$squares = returnSquarePoint($lng, $lat);
$info_sql = "select id,locateinfo,lat,lng from `lbs_info` where lat<>0 and lat>{$squares['right-bottom']['lat']} and lat<{$squares['left-top']['lat']} and lng>{$squares['left-top']['lng']} and lng<{$squares['right-bottom']['lng']} ";

//计算两个坐标的直线距离

   public function getDistance($lat1, $lng1, $lat2, $lng2){
$earthRadius =6378.137; //近似地球半径米 // 转换为弧度 $lat1 = ($lat1 * pi()) / 180; $lng1 = ($lng1 * pi()) / 180; $lat2 = ($lat2 * pi()) / 180; $lng2 = ($lng2 * pi()) / 180; // 使用半正矢公式 用尺规来计算 $calcLongitude = $lng2 - $lng1; $calcLatitude = $lat2 - $lat1; $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2); $stepTwo = 2 * asin(min(1, sqrt($stepOne))); $calculatedDistance = $earthRadius * $stepTwo; return round($calculatedDistance); }

在lat和lng上建立一个联合索引后,使用此项查询,每条记录的查询消耗平均为0.8毫秒,相比以前的1700ms,真的是天壤之别啊。效率真真的是以前的2125倍~~

总结:这应该也不是效率最好的办法,但是效率比以前确实有明显的提升。请记住,总有办法更好的。

php根据地址的经纬度查询周围的城市例子的更多相关文章

  1. 百度api查询多个地址的经纬度的问题

    在使用百度api查询多个地址的经纬度的时候,由于百度api提供的经纬度查询方法是回调函数,并且后续操作必须等经纬度获取完成才能进行,问题就存在于怎么判断所有地点是否都回调完成了,问了之前的一个前端大佬 ...

  2. 腾讯云图片鉴黄集成到C# SQL Server 怎么在分页获取数据的同时获取到总记录数 sqlserver 操作数据表语句模板 .NET MVC后台发送post请求 百度api查询多个地址的经纬度的问题 try{}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code 会 不会被执行,什么时候被执行,在 return 前还是后? js获取某个日期

    腾讯云图片鉴黄集成到C#   官方文档:https://cloud.tencent.com/document/product/641/12422 请求官方API及签名的生成代码如下: public c ...

  3. 百度地图JavaScript API经纬度查询-MAP

    百度地图JavaScript API经纬度查询-MAP-ABCDEFGHIJKMHNOPQRSTUVWXYZ: 搜索:<input type="text" size=&quo ...

  4. 个人永久性免费-Excel催化剂功能第92波-地理地址与经纬度互转功能

    GPS设备和手机LBS的兴起,在地理信息存储过程中,在程序.应用级别是需要用经纬度去定位,而在数据分析的级别,特别是省市区镇街的分析,用到的是人可识别的文本类型存储,从设备中采集下来的数据和人工维护的 ...

  5. 高德地图api实现地址和经纬度的转换(python)

    利用高德地图web服务api实现地理/逆地址编码 api使用具体方法请查看官方文档 文档网址:http://lbs.amap.com/api/webservice/guide/api/georegeo ...

  6. 通过地址获得经纬度(百度Geocoding API)

    1.什么是Geocoding? Geocoding API 是一类简单的HTTP接口,用于提供从地址到经纬度坐标或者从经纬度坐标到地址的转换服务,用户可以使用C# .C++.Java等开发语言发送HT ...

  7. 利用百度API(js),怎样通过地址获取经纬度

    根据经纬度找到具体地址:http://api.map.baidu.com/geocoder?location=纬度,经度&output=输出格式类型&key=用户密钥如:http:// ...

  8. 使用百度地图API自动获取地址和经纬度

    先上效果图,这是直接点击获取经纬度和地址的.没有做搜索的功能. 代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitiona ...

  9. 根据经纬度查询最近距离,mysql查询经纬度附近范围

    public class Test{ private static List<LocalAddress> ilist = new ArrayList<LocalAddress> ...

随机推荐

  1. VBoxManage命令

    查看有哪些虚拟机VBoxManage list vms 查看虚拟的详细信息VBoxManage list vms –long 查看运行着的虚拟机VBoxManage list runningvms 开 ...

  2. 国内好用的maven仓库,添加到本地nexus中

    速度快的maven仓库真的难找,国外网站很多被封了.前天在网上找,有人说阿里有个公开的,速度很快.确实很快! 我添加到本地nexus中,结果不能更新索引,我是这么添加的,如下: 这个地址在浏览器中能够 ...

  3. 2016年4月21百度iOS实习生在线笔试题&编程题

    1.一个人上台阶可以一次上1个,2个,或者3个,问这个人上32层的台阶,总共有几种走法? 思路:先建立数学模型,设3步的走 i 次,2步的走 j 次, 1步的走 k 次,上了3*i + 2*j + 1 ...

  4. 使用dubbo分布式服务框架发布服务及消费服务

    什么是DUBBO DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案. 准备工作 安装zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服 ...

  5. gcc编译过程简述

    在linux系统上,从源文件到目标文件的转化是由编译器完成的.以hello.c程序的编译为例,如下: dfcao@linux: gcc -o hello hello.c 在这里,gcc编译器读取源文件 ...

  6. 在Windows Server 2012 R2中搭建SQL Server 2012故障转移集群

    需要说明的是我们搭建的SQL Server故障转移集群(SQL Server Failover Cluster)是可用性集群,而不是负载均衡集群,其目的是为了保证服务的连续性和可用性,而不是为了提高服 ...

  7. DMZ区

    DMZ是英文“Demilitarized Zone”的缩写,它是为了解决安装防火墙后外部网络不能访问内部网络服务器的问题,而设立的一个非安全系统与安全系统之间的缓冲区,这个缓冲区位于企业内部网络和外部 ...

  8. Android 实现网络多线程APK文件下载

    (转自:http://blog.csdn.net/mad1989/article/details/38421465) 实现原理 (1)首先获得下载文件的长度,然后设置本地文件的长度. (2)根据文件长 ...

  9. Linux学习笔记1_用户和权限

    自从我大微软终于放下身段,决定给开源社区一个迟来的拥抱,追随多年的拥趸们像是突然得到了女神的垂青,各种茫然失措.痛哭流涕.欢欣鼓舞,纷纷唱了起来:“等了好久终于等到今天,梦了好久终于把梦实现……”唱完 ...

  10. WPFTookit Chart 高级进阶

    数据源增加SeriesSource 使用方式 <Charting:Chart x:Name="chart" Helper:ChartHelper.DependentValue ...