在起点半径范围内计算位置的最佳方法

时间:2009-07-28 13:05:45

标签: php mysql postal-code haversine

我的目标是在我的最新项目中创建一个功能,最好使用PHP。当每个用户注册时,他们将输入他们的邮政编码。然后希望我将使用Open Street Map将其转换为lat / long。

无论如何,我希望能够找到当前用户附近的其他用户。 我见过很多人使用Haversine公式,但是这意味着用户会询问每个其他用户的详细信息以计算出距离。我可以对此进行缓存,但随着新用户注册,它很快就会过时。

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

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";

这是从某人的博客中提取的。

我没有关于注册率或用户数量的数据,因为它仍在开发中。

我很感激我可以用来查找特定半径内匹配用户的任何反馈或其他方法。

3 个答案:

答案 0 :(得分:2)

版本4.1中的mySql有GIS和Spatial Extensions,请参阅here。从您将要找到的描述中,它可用于解决您遇到的问题:

  

GIS(地理信息系统)   存储并查找具有的对象   一个或多个空间属性,例如   作为大小和位置,并习惯   处理这些对象。一个简单的例子   将是一个存储的系统   使用地理位置的城镇地址   坐标。如果这相当静止   然后将数据与其他数据相结合   信息,例如a的位置   出租车,然后可以使用这些数据   找到最近的出租车到某个   位置。

它向MySql添加了几个东西,如:

  • 空格键和POINT类型:

    CREATE TABLE地址(   地址CHAR(80)NOT NULL,   address_loc POINT NOT NULL,   PRIMARY KEY(地址),    SPATIAL KEY(address_loc) );

  • 转化套路

    INSERT INTO地址VALUES('Foobar street 12', GeomFromText('POINT(2671 2500)'));

  • GIS计算功能

    选择   c.cabdriver,   ROUND(的 GLength(LineStringFromWKB(线段形式(AsBinary(c.cab_loc),                                              AsBinary(a.address_loc)))))     距离 从驾驶室c,地址a 按距离排序ASC LIMIT 1;

(从上面的链接中取得的例子)

答案 1 :(得分:1)

如果您愿意放宽“在一定范围内”的定义而不是特定的圆形,则可以大大简化问题。如果简化为“方形”,则可以在“半径”内找到所有位置,其中包含2个简单的“之间”子句(一个用于长度为lat)。例如:

SELECT * FROM location WHERE
  lat BETWEEN (my_lat - radius) AND (my_lat + radius)
  AND long BETWEEN (my_long - radius) AND (my_long + radius);

当然,在使用更准确的方法计算实际距离之前,可以使用此选项来选择位置的子集。

答案 2 :(得分:0)

不可否认,这是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>

哦,VELatLong对象来自Virtual Earth API(http://msdn.microsoft.com/en-us/library/bb412519.aspx),但基本上是一个美化的结构,所以你应该能找到合适的替代品