longitude と latitudeを使って、ある地点からのロケーションベースのソートをしたいときってありますね。
そこで使えそうなのが
Haversine formulaという公式です。
http://en.wikipedia.org/wiki/Haversine_formula
これが最終的に
のように展開できます。(式の展開などはめんどくさいので省略します。。。)
この公式をpostgresql上で実際のサンプルデータを使ってみてみると
select id,name,latitude,longitude from cities;
id | name | latitude | longitude
----+--------------+-----------+------------
3 | Kuala Lumpur | 3.146685 | 101.686656
1 | Singapore | 1.283333 | 103.833333
4 | Bangkok | 13.787395 | 100.524502
5 | Hong Kong | 22.27833 | 114.15889
6 | Jakarta | -6.193806 | 106.843314
2 | Manila | 14.603352 | 120.984682
7 | Taipei | 25.105495 | 121.557441
このサンプルデータを使い、Kuala Lumpurに近いポイントでソートしてみます。
\set lat 3.046685
\set lng 100.686656
SELECT
id,name,latitude,longitude ,
3956 * 2 *
ASIN(
SQRT(
POWER(SIN( (:lat - abs(cities.latitude)) * pi()/180 / 2), 2 ) + COS(:lat * pi()/180) * COS(cities.latitude * pi()/180) * POWER(SIN((:lng - cities.longitude) * pi()/180 / 2)
, 2)
)
)
AS distance
from cities order by distance;
id | name | latitude | longitude | distance
----+--------------+-----------+------------+------------------
3 | Kuala Lumpur | 3.146685 | 101.686656 | 69.2892623017496
1 | Singapore | 1.283333 | 103.833333 | 248.908449383448
6 | Jakarta | -6.193806 | 106.843314 | 476.126654090718
4 | Bangkok | 13.787395 | 100.524502 | 741.677180652493
2 | Manila | 14.603352 | 120.984682 | 1595.9777030627
5 | Hong Kong | 22.27833 | 114.15889 | 1605.53830449593
7 | Taipei | 25.105495 | 121.557441 | 2059.91627144258
このような結果になりました。
*パフォーマンス等は考慮してません。。。