Php 计算起点半径内位置的最佳方法

Php 计算起点半径内位置的最佳方法,php,mysql,postal-code,haversine,Php,Mysql,Postal Code,Haversine,我的目标是在我的最新项目中创建一个特性,最好使用PHP。当每个用户注册时,他们将输入他们的邮政编码。然后,希望我能用开放式街道地图将其转换为lat/long 无论如何,我希望能够找到位于当前用户附近的其他用户。 我见过很多人使用哈弗森公式,但是这意味着用户查询其他用户的详细信息来计算距离。我可以缓存这个,但随着新用户的注册,它很快就会过时 运行以下查询会对我的系统产生什么样的影响 sql = "SELECT zipcode, ( 3959 * acos( cos( radians( {$coor

我的目标是在我的最新项目中创建一个特性,最好使用PHP。当每个用户注册时,他们将输入他们的邮政编码。然后,希望我能用开放式街道地图将其转换为lat/long

无论如何,我希望能够找到位于当前用户附近的其他用户。 我见过很多人使用哈弗森公式,但是这意味着用户查询其他用户的详细信息来计算距离。我可以缓存这个,但随着新用户的注册,它很快就会过时

运行以下查询会对我的系统产生什么样的影响

sql = "SELECT zipcode, ( 3959 * acos( cos( radians( {$coords['latitude']} ) ) 
    * cos( radians( latitude ) ) * cos( radians( longitude ) 
    - radians( {$coords['longitude']} ) ) 
    + sin( radians( {$coords['latitude']} ) ) * sin( radians( latitude ) ) ) ) 
    AS distance FROM zipcodes HAVING distance <= {$radius} ORDER BY distance";
sql=“选择zipcode,(3959*acos(弧度({$coords['latitude']}))
*cos(弧度(纬度))*cos(弧度(经度)
-弧度({$coords['longitude']}))
+sin(弧度({$coords['latitude']}))*sin(弧度(latitude)))

由于与zipcodes的距离在4.1版中有对mySql的GIS和空间扩展,请参见。从描述中,您会发现,它用于解决您在这里遇到的问题:

地理信息系统 存储和查找具有 一个或多个空间属性,例如 作为大小和位置,用于 处理这样的对象。一个简单的例子 将是一个存储 使用地理信息的城镇地址 坐标,如果这是静态的 然后将数据与其他数据结合起来 信息,例如 出租车司机室,然后可以使用此数据 找到离某个特定区域最近的驾驶室 地点

它为MySql添加了以下内容:

  • 空间关键点和点类型:

    创建表地址( 地址字符(80)不为空, 地址_loc不为空, 主键(地址), 空间键(地址) ))

  • 转换例程

    插入地址值('Foobar street 12',GeomFromText('POINT(2671 2500)')

  • GIS计算功能

    挑选 c、 出租车司机, 圆形(GLength(线条从WKB(线条(二进制)(c.cab_loc)), AsBinary(a.address_loc)))) 作为距离 从c驾驶室,地址a 按距离订购ASC限制1


(上面链接中的例子)

不可否认,这是Javascript而不是PHP,但我认为转换起来很简单

它计算两个点之间的距离,计算地球的曲率。不久前在物流应用程序中使用它,然后用使用正确道路路线的代码替换它

它可能对你有用

<script type="text/javascript">
function getDistance(lat1,lng1,lat2,lng2)
 {
  p1 = new VELatLong(lat1,lng1);
  p2 = new VELatLong(lat2,lng2);
  miles = true;
  p1.Latitude= latLonToRadians(p1.Latitude);
  p1.Longitude= latLonToRadians(p1.Longitude);
  p2.Latitude= latLonToRadians(p2.Latitude);
  p2.Longitude= latLonToRadians(p2.Longitude);
  var R = 6371; // earth's mean radius in km
  var dLat  = p2.Latitude- p1.Latitude;
  var dLong = p2.Longitude- p1.Longitude;
  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
  Math.cos(p1.Latitude) * Math.cos(p2.Latitude) * Math.sin(dLong/2) * 
Math.sin(dLong/2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  var disKm = R * c;
  var disMiles = disKm * 0.6214;
  alert (miles ? disMiles : disKm);
 }
 //  convert lat/long in degrees to radians
 function latLonToRadians( point )
 {
  return point * Math.PI / 180;
 }
</script>

函数getDistance(lat1、lng1、lat2、lng2)
{
p1=新高度(lat1,lng1);
p2=新的VELatLong(lat2,lng2);
英里=真;
p1.纬度=纬度(p1.纬度);
p1.经度=纬度(p1.经度);
p2.纬度=纬度(p2.纬度);
p2.经度=拉特朗托拉迪安(p2.经度);
var R=6371;//地球的平均半径,单位为km
var dLat=p2.纬度-p1.纬度;
var dLong=p2.经度-p1.经度;
变量a=Math.sin(dLat/2)*Math.sin(dLat/2)+
数学cos(p1.纬度)*数学cos(p2.纬度)*数学sin(dLong/2)*
sin数学(dLong/2);
var c=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));
var disKm=R*c;
var disMiles=disKm*0.6214;
警报(英里数?disMiles:disKm);
}
//将以度为单位的横向/纵向转换为弧度
函数拉托拉迪安(点)
{
返回点*Math.PI/180;
}

哦,VELatLong对象来自虚拟地球API(),但基本上是一个美化的结构,因此您应该能够找到合适的替代品

如果您愿意将“在某个半径内”的定义放宽为非特定的圆,那么问题可以大大简化。如果您简化为“正方形”,您可以通过2个简单的“between”子句(一个用于lat,一个用于long)找到“radius”内的所有位置。例如:


当然,在使用更精确的方法计算到位置的实际距离之前,这可以用于选择位置的子集。

除了坐标系在二维基础上工作之外。我查了一下格伦特,没有任何迹象表明它考虑了球面几何,或者做了毕达哥拉斯定理以外的计算。这看起来很棒!今晚下班后我要把它读一遍,看起来就像我要找的一样!真的。。。虽然从技术上讲,这歧视了生活在国际日期线或南北极附近的人:-)是的,这就是为什么“广场”在引号中的原因:)这也很棒!我很久以前从未与lat合作过。因此,理论上,我可以对从中返回的数据子集执行我的原始查询。好主意!半径的单位是米、公里还是英里?它的单位是度(或弧度,取决于数据的存储方式?)。你必须做一些数学运算才能从公里/英里换算。纬度很容易转换,经度有点复杂,但是你应该可以很容易地找到公式。OP不是问PHP(标签可能应该被删除),而是问SQL查询。实际上,他确实说过他想用PHP实现这个项目,并且他对在指定半径内匹配用户的任何方法都感兴趣。感谢您的输入。这不完全是我想要的,但它确实让我对所需的数学有了一点了解!这真的是一个与PHP相关的问题吗?听起来更像是SQL问题。首选语言是PHP/MySQL。我不知道是否有任何使用PHP实现这一点的方法可能被忽略了(显然除了在PHP中执行计算之外)。
SELECT * FROM location WHERE
  lat BETWEEN (my_lat - radius) AND (my_lat + radius)
  AND long BETWEEN (my_long - radius) AND (my_long + radius);