たくあんポリポリ

勉強したことを載せていきます。最近、技術系の記事はZennに書いています。(https://zenn.dev/chittai)

Google Places API とGeocoderで最も近いコンビニまでの距離を計算する

今回は、Google Places APIを使用してWebアプリケーションを作ることがあったので、その時の知見をログとして残します。

今回実施したこと

住所・地域・建物を検索した時に、その検索結果の場所から一番近いコンビニの情報を持ってくる。そして、そのコンビニまでの距離を計算する。GoogleAPIJavaScriptで書かれているため、どうやってRubyで計算するかと考えた時に、Geocoderなるものを見つけたので使用してみた。

github.com

処理の内容

コンビニの情報に指定して情報を取得する方法。APIからjson形式で情報をとってくる際に、URIに「types」属性を指定する。このtypesではコンビニ(convenience_store)や病院(hospital)などを指定することができる。属性の一覧については、以下のURLに記載されている。

プレイスタイプ  |  Google Places API  |  Google Developers

APIで取得した結果は距離順にソートされているわけではないので、さらに「rankby=distance」を指定することで距離順にソートされた状態で情報を取得できる。

SampleCode

緯度・経度の計算はGeocoderを使用して求めている。しかし、コンビニの緯度・経度はAPIから取得した情報から格納している。その二つの情報をGeocoder::Calculations.distance_betweenメソッドに引数として渡すことで二点間の距離を求めている。

require 'net/https'
require 'uri'
require 'json'
require 'geocoder'

API_KEY = 'YOUR_API_KEY'
SEARCH_KEY = '六本木'

types = 'convenience_store'
 
 language = 'ja'
 
 #Geocoderの設定
 Geocoder.configure(language: :ja, units: :km)
 
 #formから入力した場所の住所
 address = Geocoder.address(SEARCH_KEY)
 
 #緯度・経度の情報
 lat = Geocoder.search(SEARCH_KEY)[0].geometry['location'].values[0]
 lng = Geocoder.search(SEARCH_KEY)[0].geometry['location'].values[1]
 place_ll = Geocoder.search(SEARCH_KEY)[0].geometry['location'].values.join(',')
 
 #検索のためのURI
 uri = URI.parse "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=#{lat},#{lng}&types=#{types}&sensor=false&rankby=distance&language=ja&key=#{API_KEY}"
 
request = Net::HTTP::Get.new(uri.request_uri)
 response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
   http.request(request)
 end

 #jsonをresultsに格納
 body = JSON.parse(response.body)
 results = body['results']
 
 results.each_with_index do |place, idx|
  location = place['geometry']['location']
   lat, lng = location['lat'], location['lng']
   #自分が入力した住所と、その近辺にあるコンビニとの距離を求めている。(kmなので、四捨五入と1000掛けでmに変更する)
   distance = Geocoder::Calculations.distance_between(place_ll,"#{lat},#{lng}").round(3)*1000
   p "#{idx+1}: #{place['name']}: #{distance}m "
 end 

Result

概ね間違っていないが、「5」と「6」に逆転現象が起きている。実際にGoogleMAPで調べてみると確かに6の方が遠くになっている。これは、Geocoderの距離の計算の仕方がGoogleの計算方法と異なるため起きていると思われる。もしGoogleAPIで二点間の距離を求めたい場合はGoogle Maps Directions API にて求めることができる。

"1: セブンイレブン 六本木七丁目店: 59.0m "
"2: ファミリーマート六本木七丁目店: 129.0m "
"3: セブン−イレブンセントラム六本木店: 134.0m "
"4: ローソン 六本木七丁目店: 190.0m "
"5: ミニストップ 六本木四丁目店: 216.0m "
"6: ファミリーマート 六本木通り店: 205.0m "
"7: セブンイレブン 東京ミッドタウン店: 223.0m "
"8: ファミリーマート・赤坂九丁目店: 250.0m "
"9: ローソン メトロス六本木店: 251.0m "
"10: ファミリーマート六本木ヒルズ店: 262.0m "
"11: ファミリーマート六本木ミッドタウン店: 249.0m "
"12: デイリーヤマザキ 六本木交差点店: 286.0m "
"13: ナチュラルローソン 乃木坂店: 298.0m "
"14: ファミリーマート 六本木交差点南店: 307.0m "
"15: ローソン EXタワープラス店: 306.0m "
"16: ファミリーマート六本木駅前店: 331.0m "
"17: ナチュラルローソン 六本木ヒルズ店: 407.0m "
"18: セブン−イレブン 六本木5丁目店: 420.0m "
"19: セブンイレブン 西麻布3丁目六本木通り店: 446.0m "
"20: ファミリーマート 六本木六丁目店: 409.0m "