8

これが私の問題です。場所と緯度/経度を含むSQLiteテーブルがあります。基本的に私はする必要があります:

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

HAVERSINE()は、緯度と経度の値のペアが与えられた場合に大圏距離(マイルまたはkm)を返すPHP関数です。これらのペアの1つはPHPによって提供され、もう1つのペアはテーブルで使用可能な各緯度/経度の行によって提供される必要がありlocationsます。

SQLiteにはGeoSpatial拡張機能がないため(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();

PHPからのデータとテーブルデータを同時に処理するSQLiteユーザー定義関数を取得する方法を理解するのに問題があります。SQLiteUDFを理解しながらこの問題を解決するのを誰かが助けてくれれば幸いです(大きな勝利) SQLite IMOの)少し良くなりました。

前もって感謝します!

4

3 に答える 3

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;

誰かがより良い解決策を考えることができるなら、私に知らせてください。


この興味深いリンクを見つけました。明日試してみます。

于 2010-01-18T07:51:41.180 に答える
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);
于 2013-04-16T09:06:56.147 に答える
0

Alixの答えを構築する...

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

これにより、質問で指定したクエリが機能するようになると思います。

于 2010-01-18T14:55:09.050 に答える