Pergunta

Aqui está o meu problema, eu tenho uma tabela sqlite com locais e latitudes / longitudes. Basicamente, eu preciso:

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

HAVERSINE() é uma função PHP isso deve devolver o Grande distância do círculo (em milhas ou km) Dado um par de valores de latitude e longitude. Um desses pares deve ser fornecido pelo PHP e o outro par deve ser fornecido por cada linha de latitude / longitude disponível no locations tabela.

Como o sqlite não possui nenhuma extensão espacial geográfica (Até onde sei Espacialito existe mas ainda assim ...) Suponho que a melhor abordagem seria usar uma função personalizada com um dos métodos DOP:

Eu acho que neste caso PDO::sqliteCreateFunction() Seria suficiente, no entanto, minha experiência limitada com essa função pode ser reduzida aos casos de uso semelhantes aos fornecidos no manual do 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();

Estou tendo problemas para descobrir como posso obter uma função definida pelo usuário do SQLite para Processar dados de dados de PHP e tabela ao mesmo tempo E eu apreciaria se alguém pudesse me ajudar a resolver esse problema, além de entender o SQLite UDFS (uma grande vitória do SQLite IMO) um pouco melhor.

Desde já, obrigado!

Foi útil?

Solução

Do seu "link interessante".

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

Então faça uma consulta com:

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

Editar (por QOP): Eu finalmente precisava disso novamente e essa solução funcionou muito bem, acabei modificando um pouco o código é um pouco menos detalhado e lida com valores não numéricos graciosamente, aqui está:

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

Outras dicas

Até agora eu só conseguia pensar nesta solução:

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

E depois execute a seguinte consulta longa:

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;

Se alguém puder pensar em uma solução melhor, por favor me avise.


Eu acabei de Encontrei este link interessante, Vou tentar amanhã.

Construindo a resposta de Alix ...

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

Eu imagino que isso permitiria a consulta que você especificou em sua pergunta para funcionar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top