2010-01-17 70 views
6

这是我的问题,我有一个包含位置和纬度/经度的SQLite表。基本上我需要:用SQLite计算大圆距离

SELECT location, HAVERSINE(lat, lon) AS distance FROM location ORDER BY distance ASC; 

HAVERSINE()是一个PHP函数应该返回Great-Circle Distance(英里或公里)给出一对纬度和经度值。 其中一对应由PHP提供,另一对应由locations表中可用的每个纬度/经度行提供。

由于SQLite不具有任何地理空间扩展(据我所知SpatiaLite存在,但仍...)我猜最好的办法是使用自定义功能与任一PDO方法之一:

我觉得这种情况下PDO::sqliteCreateFunction()就足够了,但是我的这种功能有限的经验可以降低到类似PHP手册中提供了一个使用情况:

$db = new PDO('sqlite:geo.db'); 

function md5_and_reverse($string) { return strrev(md5($string)); } 

$db->sqliteCreateFunction('md5rev', 'md5_and_reverse', 1); 
$rows = $db->query('SELECT md5rev(filename) FROM files')->fetchAll(); 

我有一些麻烦搞清楚怎样才能得到一个SQLite用户定义函数为同时处理来自PHP和表格数据的数据,如果有人能够帮助我解决这个问题,同时也理解SQLite UDF(SQLite IMO的一个大胜),我会感激一点。

在此先感谢!

+1

你是否有没有定义自定义函数的工作方式?见http://stackoverflow.com/questions/3126830/query-to-get-records-based-on-radius-in-sqlite – Pentium10 2010-06-27 08:39:04

+0

@ Pentium10:不,不是至少一快一。检查我的其他问题:http://stackoverflow.com/questions/2096385/formulas-to-calculate-geo-proximity。 – 2010-06-27 19:27:56

回答

2

从您的“有趣的链接”。

function sqlite3_distance_func($lat1,$lon1,$lat2,$lon2) { 
    // convert lat1 and lat2 into radians now, to avoid doing it twice below 
    $lat1rad = deg2rad($lat1); 
    $lat2rad = deg2rad($lat2); 
    // apply the spherical law of cosines to our latitudes and longitudes, and set the result appropriately 
    // 6378.1 is the approximate radius of the earth in kilometres 
    return acos(sin($lat1rad) * sin($lat2rad) + cos($lat1rad) * cos($lat2rad) * cos(deg2rad($lon2) - deg2rad($lon1))) * 6378.1; 
} 

$db->sqliteCreateFunction('DISTANCE', 'sqlite3_distance_func', 4); 

然后做查询用:

"SELECT * FROM location ORDER BY distance(latitude,longitude,{$lat},{$lon}) LIMIT 1" 

EDIT(由QOP):我终于又需要这一点,该方案制定了伟大的,我刚刚结束了修改代码有点稍微冗长,并且优雅地处理非数字值,这里是:

$db->sqliteCreateFunction('distance', function() { 
    if (count($geo = array_map('deg2rad', array_filter(func_get_args(), 'is_numeric'))) == 4) { 
     return round(acos(sin($geo[0]) * sin($geo[2]) + cos($geo[0]) * cos($geo[2]) * cos($geo[1] - $geo[3])) * 6378.14, 3); 
    } 

    return null; 
}, 4); 
+0

在赤道或格林威治附近工作吗? – 2015-02-12 22:15:47

+0

此功能是以英里还是公里返回? – 2016-07-27 20:08:11

9

到目前为止,我只能想到这个解决方案:

$db = new PDO('sqlite:geo.db'); 

$db->sqliteCreateFunction('ACOS', 'acos', 1); 
$db->sqliteCreateFunction('COS', 'cos', 1); 
$db->sqliteCreateFunction('RADIANS', 'deg2rad', 1); 
$db->sqliteCreateFunction('SIN', 'sin', 1); 

然后执行以下冗长查询:

SELECT "location", 
     (6371 * ACOS(COS(RADIANS($latitude)) * COS(RADIANS("latitude")) * COS(RADIANS("longitude") - RADIANS($longitude)) + SIN(RADIANS($latitude)) * SIN(RADIANS("latitude")))) AS "distance" 
FROM "locations" 
HAVING "distance" < $distance 
ORDER BY "distance" ASC 
LIMIT 10; 

如果有人能想到更好的解决方案,请让我知道。


我只是found this interesting link,我来试试,明天。

0

建立关阿利克斯的答案...

$db->sqliteCreateFunction('HAVERSINE', 'haversine', 2); 

我可以想象,这将让你在你的问题的工作中指定的查询。

+0

我可以用'半正弦波(“纬度”,“经度”)',但我不知道如何使用'$ latitude'和'$ longitude' PHP变量的工作。 – 2010-01-18 15:08:57