基于LBS的应用中,可能有如下命题:给定一个坐标点(如:lat=32.0330891313519 ,lng=118.736731410027),查找出给定距离(如1公里)内的其它坐标(人,商家或别的有坐标信息的事物)。
一、直接在mysql中过滤有如下方法:
DELIMITER // CREATE FUNCTION geo_distance(lat1 FLOAT, lng1 FLOAT, lat2 FLOAT, lng2 FLOAT) RETURNS FLOAT BEGIN DECLARE r INT DEFAULT 6378137; DECLARE s0 FLOAT; DECLARE s1 FLOAT; DECLARE e0 FLOAT; DECLARE e1 FLOAT; DECLARE d0 FLOAT; DECLARE d1 FLOAT; SET s0 = RADIANS(lat1); SET s1 = RADIANS(lng1); SET e0 = RADIANS(lat2); SET e1 = RADIANS(lng2); SET d0 = ABS(s0 - e0); SET d1 = ABS(s1 - e1); RETURN r * 2 * ASIN(SQRT(POW(SIN(d0/2),2) + COS(s0)*COS(e0)*POW(SIN(d1/2),2))); END// DELIMITER ;
然后使用过虑条件:
SELECT * FROM table WHERE geo_distance(lat , lng, 32.0330891313519, 118.736731410027) <1000;
小表里可以使用,大表里结局是悲惨的,速度慢,效率差。
二、在PHP中过滤
我们可以把该表全部查出来,再循环对比每一条记录。
/**
* Calculate the distance between two coordinates.
* @param array $s, array(float:latitude, float: longitude)
* @param array $e, array(float:latitude, float: longitude)
* @return float, the distance
*/
function geo_distance($s, $e) {
//earth's mean radius in KM
$r = 6378.137;
$s[0] = deg2rad($s[0]);
$s[1] = deg2rad($s[1]);
$e[0] = deg2rad($e[0]);
$e[1] = deg2rad($e[1]);
$d0 = abs($s[0] - $e[0]);
$d1 = abs($s[1] - $e[1]);
$p = pow(sin($d0/2), 2) + cos($s[0]) * cos($e[0]) * pow(sin($d1/2), 2);
$ds = $r * 2 * asin(sqrt($p));
return $ds;
}
while($row = $db->fetch_array($query)) {
if(geo_distance(array($row['lat'] , $row['lng']), array(32.0330891313519, 118.736731410027))<1000){
//这里得出的就是小于1公里的点
}
}
通过实践,结局依然是悲惨的。速度慢,效率差。
三、使用sphinx
1)索引配置文件:
sql_query = SELECT RADIANS(lat) AS lat , RADIANS(lng) AS lng FROM table
sql_attr_float = lat
sql_attr_float = lng
2)生成索引
3)code:
include_once("./source/sphinxapi.php");
$sphinx = new SphinxClient();
$sphinx->SetServer('127.0.0.1' , 9312);
$sphinx->SetGeoAnchor('lat' , 'lng' , deg2rad($lat) , deg2rad($lng));
$sphinx->SetFilterFloatRange ( '@geodist', 0.0 , 1000.0, false );
$sphinx->SetLimits ( 0 , 1000);
$return = $sphinx->Query('' , "*");
这里有几个需要注意的地方我们从MAP API(如google map , map abc)里得到的坐标是这样子的:32.0330891313519, 118.736731410027。
sphinx中API函数SetGeoAnchor()四个参数都是以弧度为单位,在mysql中角度转弧度我们使用RADIANS函数,在PHP中我们使用deg2rad函数,接着,我们可以使用API过滤@geodist(单位:米)距离在1公里内数据。
此法是我在如厕时想出来的,果断使用,效率高,速度快!