東京都府中市、九段下のWEB制作会社Maromaroのブログです

2017.07.18

hoshida

CakePHP3 座標データをもとに距離で絞り込むクエリビルダ

絶賛 CakePHP 勉強中の、フロントエンドエンジニアの hoshida です。「絶賛〇〇中!」っていったい誰が絶賛しているんですかね。謎ですよね。

CakePHP3 でグルメ系サイトの API を使って Web アプリ開発をする中で、ユーザーからの距離で店舗を絞り込む機能をアプリ内で処理する必要があったのですが、ピンポイントにその機能を紹介している記事が見当たらなかったので作成しました。

Shops テーブルの lat、lng カラムにそれぞれに緯度経度の数値が入っているとします。

//ShopsController.php

//ユーザーの現在位置。最終的にはユーザーの位置情報を取得してここに突っ込んだりする。
$userlat = 35.6698401;
$userlng = 139.4797334;

//検索対象範囲。1km単位。0.1は100m。
$range = 0.1;

//Shopsテーブルを絞り込み
$search_shops = $this->Shops->find('all',
    ['conditions' =>['(6378 * acos(cos(radians('.$userlat.')) * cos(radians(lat)) * cos(radians(lng) - radians('.$userlng.')) + sin(radians('.$userlat.')) * sin(radians(lat)))) <' => $range ]]
);

現在位置の緯度と経度、レコードの緯度と経度の4つの値から2点間の距離を算出し、その値が、事前に指定した $range よりも小さいことを絞り込みの条件に設定しています。

これで、ユーザーの現在位置から100m以内のレコードが $search_shops の中に入ります。

計算部分の細かい内容は、参考にした以下のサイトで詳しく説明されています。
現在地から半径○メートル以内の○○を抽出するSQL

なお今回使った式は比較的簡単なものなのですがその分誤差があるようです。2点間の距離の算出方法は他にもあるようなので、より精度を高めたい場合は以下の記事に紹介されている別の方法をお試しください。本記事の式は「球面三角法を利用したもの」です。ただこちらの記事では単純なPHPでのコードが紹介されているので、CakePHP のクエリビルダでの記述方法は各自頑張ってください(^^;)
2つの座標間の距離を求める

 

ところで今回初めて知ったのですが、距離を計算するときには地球の丸みを計算に入れるんですね。地球球体説はアリストテレスがはじめて物理的・観察的な論拠を提出したと言われているのですが、そんな大昔の発見が今日の技術にも生きていると思うと昔の発見はすごいなあと思います。

たまにはアリストテレスに感謝しないといけませんね。

ではでは