地図座標から、地理的に距離の近い順に並び替える

By | 2015/03/09

不動産とかで緯度経度情報をデータベースに保存する事がよくありますが、「指定した場所から近い順に並び替えて出して欲しい」と言われました。

MySQLデータベースに緯度経度の数値が入っているとします。

select id, lat, lng from DATA;

+------+--------------------+---------------------+
| id   | lat                | lng                 |
+------+--------------------+---------------------+
|    1 | 35.724252400000000 | 139.658451900000040 |
|    2 | 35.713803542917470 | 139.670481322615050 |
|    3 | 35.714689200000000 | 139.649800100000000 |
|    4 | 35.711476000000000 | 139.667846100000020 |
|    5 | 35.720492134294140 | 139.638486190213030 |
|    6 | 35.694957043369100 | 139.677307838623050 |
|    7 | 35.708757914661106 | 139.682740747885200 |
|    8 | 35.728563200000000 | 139.670181299999000 |
|    9 | 35.708731429564786 | 139.670904200662800 |
|   10 | 35.724363300000000 | 139.648888800000000 |
+------+--------------------+---------------------+

座標間の距離は「三平方の定理」を使えば
斜線(地点間の距離)の2乗 = 底辺(東西方向の距離)の2乗 + 高さ(南北方向の距離)の2乗
で出せますが、今回は正確な距離を出さなくても大小関係だけ分かればよいので
底辺の2乗 + 高さの2乗
の大小を比較すればOKです。

緯度経度の引き算から距離を出そうとした場合、緯度経度の1度あたりの実際の距離は、北海道と沖縄ではかなりの違いがあり単純に比較できないのですが、今回は近所で比較するだけなので中心点の北側だろうが南側だろうが気にするほど誤差は無いだろうという判断です。

MySQLの関数はABSが絶対値、POWERが乗数です

座標(35.72052, 139.64235)から近い順に並び替えたい場合(計算結果は実数になり、ちょっと見づらいので便宜上ROUND使って小数点以下15桁ぐらいで丸めています)

select id, lat, lng, ROUND(POWER(ABS(lat - 35.72052), 2) + POWER(ABS(lng - 139.64235), 2), 15) as distance from DATA order by distance asc;

+------+--------------------+---------------------+-------------------+
| id   | lat                | lng                 | distance          |
+------+--------------------+---------------------+-------------------+
|    5 | 35.720492134294140 | 139.638486190213030 | 0.000014929802567 |
|   10 | 35.724363300000000 | 139.648888800000000 | 0.000057526860330 |
|    3 | 35.714689200000000 | 139.649800100000000 | 0.000089502218650 |
|    1 | 35.724252400000000 | 139.658451900000040 | 0.000273201993371 |
|    4 | 35.711476000000000 | 139.667846100000020 | 0.000731845051211 |
|    2 | 35.713803542917470 | 139.670481322615050 | 0.000836482107813 |
|    8 | 35.728563200000000 | 139.670181299999000 | 0.000839274325874 |
|    9 | 35.708731429564786 | 139.670904200662800 | 0.000954312768397 |
|    7 | 35.708757914661106 | 139.682740747885200 | 0.001769759166245 |
|    6 | 35.694957043369100 | 139.677307838623050 | 0.001875515232908 |
+------+--------------------+---------------------+-------------------+

意外と手こずる気がしてたのですが、さくっと簡単にできました。

※ でも、地球の丸みも考慮して、正確なキロ数を出したければ↓こうすれば良いらしいよ(※地球の直径6371km)

select id, lng, lat, (6371 * acos(cos(radians(35.72052)) * cos(radians(lat)) * cos(radians(lng) - radians(139.64235)) + sin(radians(35.72052)) * sin(radians(lat)))) as distance from DATA order by distance 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です