这是我的问题,我有一个包含位置和纬度/经度的SQLite表。基本上我需要:
SELECT location, HAVERSINE(lat, lon) AS distance FROM location ORDER BY distance ASC;
HAVERSINE()
是一个PHP函数,应该在给定一对纬度和经度值的情况下返回Great-Circle Distance(以英里或公里为单位)。 其中一对应由PHP提供,另一对应由locations
表中提供的每个纬度/经度行提供。
由于SQLite没有任何Geo Spatial扩展( AFAIK 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的一大胜利)好一点。
提前致谢!
答案 0 :(得分: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,明天我会试试。
答案 1 :(得分:3)
来自“有趣的链接”。
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"
编辑(通过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);
答案 2 :(得分:0)
建立Alix的答案......
$db->sqliteCreateFunction('HAVERSINE', 'haversine', 2);
我认为这会允许您在问题中指定的查询起作用。