Вычисление расстояния с большим циклом с помощью SQLite

Вот моя проблема, у меня есть таблица SQLite с местоположениями и широтами / долготами. В основном мне нужно:

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

HAVERSINE() – это функция PHP, которая должна возвращать расстояние Великого круга (в милях или км), учитывая пару значений широты и долготы. Одна из этих пар должна быть предоставлена ​​PHP, а другая пара должна быть предоставлена ​​каждой строкой широты / долготы, доступной в таблице locations .

Поскольку SQLite не имеет расширения Geo Spatial ( AFAIK SpatiaLite существует, но все еще … ) Я предполагаю, что лучшим подходом было бы использование пользовательской функции с одним из методов PDO:

  • PDO::sqliteCreateFunction()
  • PDO::sqliteCreateAggregate()

Я думаю, что для этого случая 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) немного лучше.

Заранее спасибо!

Из вашей «интересной ссылки».

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

До сих пор я мог только думать об этом решении:

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

Если кто-нибудь может подумать о лучшем решении, пожалуйста, дайте мне знать.


Я просто нашел эту интересную ссылку , я попробую завтра.

Исходя из ответа Аликс …

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

Я бы предположил, что это позволит задать запрос, указанный вами в вашем вопросе.