デルタ システム ソリューションズ 株式会社

Google Maps APIでお店のランキング表示をしてみると(1/3)

技術情報 JavaScript Google Maps 作り方
更新日:2019/03/22

お腹がすいた。この辺でどこか評判がいいレストランはないかな?
では、Googleで検索っと!

Googleには、お店の口コミ情報が集まっていて、5段階評価の星表示もお店選びには役立ちますね。
欲を言うなら、評価順にランキング表示ができたら、サクッと評判のよいお店が探せるのに。

ということで今回は、Google Maps APIを使用して、お店のランキング表示をしてみましょう!

お店ランキング表示の流れ

お店情報探しに、Google検索ではなく、なぜGoogle Mapsを使うかというと、Google Maps APIを使った検索では「特定のポイントから半径〇メートル圏内」といった検索指定ができるからです。
こうした位置情報を使った検索は、Google Mapsが得意ですね。

お店ランキング表示をするのに必要な機能は、
  1. 検索エリアの位置情報を取得
  2. 指定ポイントから半径〇メートルのお店情報を取得
  3. お店情報を評価順に並べて表示
です。

検索エリアの位置情報を取得

住所から位置情報(緯度・経度)を取得するには、Google Maps APIの『ジオコーディング』を使います。
こちらは以前にテーマとして取り上げていますので、詳しくは過去記事にて!

【おさらい!】
【住所⇔緯度・経度】位置情報の相互変換
皆さん、住所から緯度・経度、緯度・経度から住所を知りたいことってありますよね?
地図と連携したサービスを考えた時には必要になってくる技術です。
今回はGoogle Maps APIを使用して、住所変換遊びをしてみましょ!
技術情報 JavaScript Google Maps 作り方


指定ポイントから半径〇メートルのお店情報を取得

お店情報を検索するのに必要なAPIは
  • Map JavaScript API
  • Places API
  • Geocoding API
です。

お店検索をするAPIの本体は「Places API」ですが、JavaScriptから呼び出すために「Map JavaScript API」を介すことになります。
「Geocoding API」は、住所から緯度・経度を取得するのに必要です。
Google API コンソールで、この3つを有効化しておきましょう。

では、指定のポイントから半径〇メートル以内のお店情報を取得していきます!

【サンプル1】
 住所(ランドマーク名)→緯度・経度→お店情報

検索場所:
KeyWord:
★結果★


【サンプル1ソース】

<table>
<tr>
  <td>検索場所:</td><td><input type="text" id="addressInput" value="近江町市場" style="width: 200px"></td>
</tr>
<tr>
  <td>KeyWord:</td><td><input type="text" id="keywordInput" value="寿司" style="width: 200px"></td>
</tr>
<tr>
  <td colspan="2" style="padding: 10px">
    <input type="button" value="お店情報取得" onclick="getPlaces();">
  </td>
</tr>
</table>

★結果★<br />
<div id="results" style="width: 100%; height: 200px; border: 1px dotted; padding: 10px; overflow-y: scroll;"></div>

<script src="https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxx&libraries=places"></script>
<script type="text/javascript">
var placesList;

/*
 お店情報取得
*/
function getPlaces(){
  
  //結果表示クリア
  document.getElementById("results").innerHTML = "";
  //placesList配列を初期化
  placesList = new Array();
  
  //入力した検索場所を取得
  var addressInput = document.getElementById("addressInput").value;
  if (addressInput == "") {
    return;
  }
  
  //検索場所の位置情報を取得
  var geocoder = new google.maps.Geocoder();
  geocoder.geocode(
    {
      address: addressInput
    },
    function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        //取得した緯度・経度を使って周辺検索
        startNearbySearch(results[0].geometry.location);
      }
      else {
        alert(addressInput + ":位置情報が取得できませんでした。");
      }
    });
}

/*
 位置情報を使って周辺検索
  latLng : 位置座標(google.maps.LatLng)
*/
function startNearbySearch(latLng){
  
  //読み込み中表示
  document.getElementById("results").innerHTML = "Now Loading...";
  
  //Mapインスタンス生成
  var map = new google.maps.Map(
    document.createElement("div")
  );
  
  //PlacesServiceインスタンス生成
  var service = new google.maps.places.PlacesService(map);
  
  //入力したKeywordを取得
  var keywordInput = document.getElementById("keywordInput").value;
  
  //周辺検索
  service.nearbySearch(
    {
      location: latLng,
      radius: 800,
      type: ['restaurant'],
      keyword: keywordInput
    },
    displayResults
  );
}

/*
 周辺検索の結果表示
 ※nearbySearchのコールバック関数
  results : 検索結果
  status : 実行結果ステータス
  pagination : ページネーション
*/
function displayResults(results, status, pagination) {
    
  if(status == google.maps.places.PlacesServiceStatus.OK) {
  
    //検索結果をplacesList配列に連結
    placesList = placesList.concat(results);
    
    //pagination.hasNextPage==trueの場合、
    //続きの検索結果あり
    if (pagination.hasNextPage) {
      
      //pagination.nextPageで次の検索結果を表示する
      //※連続実行すると取得に失敗するので、
      //1秒くらい間隔をおく
      setTimeout(pagination.nextPage(), 1000);
    
    //pagination.hasNextPage==falseになったら
    //全ての情報が取得できているので、
    //結果を表示する
    } else {
      
      //placesList配列をループして、
      //結果表示のHTMLタグを組み立てる
      var resultHTML = "<ol>";
      
      for (var i = 0; i < placesList.length; i++) {
        place = placesList[i];
        
        //ratingがないのものは「---」に表示変更
        var rating = place.rating;
        if(rating == undefined) rating = "---";
        
        //表示内容(評価+名称)
        var content = "【" + rating + "】 " + place.name;
        
        resultHTML += "<li>";
        resultHTML += content;
        resultHTML += "</li>";
      }
      
      resultHTML += "</ol>";
      
      //結果表示
      document.getElementById("results").innerHTML = resultHTML;
    }
  
  } else {
    //エラー表示
    var results = document.getElementById("results");
    if(status == google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
      results.innerHTML = "検索結果が0件です。";
    } else if(status == google.maps.places.PlacesServiceStatus.ERROR) {
      results.innerHTML = "サーバ接続に失敗しました。";
    } else if(status == google.maps.places.PlacesServiceStatus.INVALID_REQUEST) {
      results.innerHTML = "リクエストが無効でした。";
    } else if(status == google.maps.places.PlacesServiceStatus.OVER_QUERY_LIMIT) {
      results.innerHTML = "リクエストの利用制限回数を超えました。";
    } else if(status == google.maps.places.PlacesServiceStatus.REQUEST_DENIED) {
      results.innerHTML = "サービスが使えない状態でした。";
    } else if(status == google.maps.places.PlacesServiceStatus.UNKNOWN_ERROR) {
      results.innerHTML = "原因不明のエラーが発生しました。";
    }

  }
}
</script>

サンプル1では、金沢・近江町市場の中心地から半径800m以内のレストラン情報を、「寿司」に絞って検索してみました。
リストに表示しているのは、rating(評価)とname(名称)です。
この時点では、評価順に並んでいないことに注目してください。

サンプル1のソースをポイント解説していきます。
[18行目]
<script src="https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxx&libraries=places"></script>
「Map JavaScript API」から「Places API」を利用するために、「libraries=places」のパラメータが必要です。


[64〜70行目]
//Mapインスタンス生成
var map = new google.maps.Map(
  document.createElement("div")
);

//PlacesServiceインスタンス生成
var service = new google.maps.places.PlacesService(map);
new google.maps.places.PlacesServiceで「Places API」のサービスインスタンスを生成します。引数は、new google.maps.Mapで取得したMapインスタンスです。

new google.maps.Mapの引数は、地図表示するためのコンテナとなるdivタグですが、画面に地図を表示する必要がない場合は、「document.createElement("div")」で生成したdiv要素を渡すだけで事足ります。


[75〜84行目]
//周辺検索
service.nearbySearch(
  {
    location: latLng,
    radius: 800,
    type: ['restaurant'],
    keyword: keywordInput
  },
  displayResults
);
指定範囲周辺の施設情報を取得するには、「Places API」のnearbySearchを使用します。
第1引数は検索条件を、第2引数にはコールバック関数を指定します。

<検索条件>
location : 中心位置の緯度・経度(google.maps.LatLng)を指定します。指定必須です。
radius:検索範囲を半径〇メートルで指定します。指定必須です。
type:施設情報の種類を指定します。
keyword:キーワードを指定して、検索結果を絞り込むことができます。

typeに指定できる種類は複数あるので、目的に合わせて指定するとよいでしょう。
⇒ Place Types Places API Google Developers


[94行目]
function displayResults(results, status, pagination) {
nearbySearchのコールバック関数の引数は、
  • results:検索結果
  • status:実行結果ステータス
  • pagination:ページネーション
です。

resultsに、検索結果の施設情報が『連想配列』で格納されています。
<連想配列イメージ>
results[0].name = "あいうえ寿司"
results[0].rating = 3.5
  :
results[1].name = "かきくけ寿司"
results[1].rating = 4.0


[96〜113行目]
if(status == google.maps.places.PlacesServiceStatus.OK) {

  //検索結果をplacesList配列に連結
  placesList = placesList.concat(results);
  
  //pagination.hasNextPage==trueの場合、
  //続きの検索結果あり
  if (pagination.hasNextPage) {
    
    //pagination.nextPageで次の検索結果を表示する
    //※連続実行すると取得に失敗するので、
    //  1秒くらい間隔をおく
    setTimeout(pagination.nextPage(), 1000);
    
  //pagination.hasNextPage==falseになったら
  //全ての情報が取得できているので、
  //結果を表示する
  } else {
実行結果ステータスが"OK"ならば、1件以上の検索結果が取得できています。

ここで、nearbySearchの検索結果の仕様で、知っておかなければいけない重要なポイントがあります。
それは「最大60件取得可能で、20件ずつ取得される」ということ。
21件目以降は、コールバック関数の第3引数であるpagination(ページネーション)を使って、ページ遷移しながら取得する必要があります。

pagination.hasNextPage==trueの場合、「続きの検索結果あり」と判断できます。
今取得できた検索結果をplacesList配列変数に退避しつつ、pagination.nextPage()をコールします。
すると、コールバック関数(displayResults)が再び呼び出され、その時のresultsの内容が次の20件のものに変化しているという仕組みです。

pagination.nextPage()を実行する時は、「連続実行すると上手く取得できない」というハマリポイントがあるのでご注意を!
setTimeoutなどを使って、1秒くらい間隔をおいて実行すると上手くいきます。

pagination.nextPage()を繰り返し、pagination.hasNextPage==falseになったら、「全ての情報が取得できた」と判断できます。
placesList配列変数に記憶しておいた検索結果から、HTMLを組み立てて表示します。

さて、「Places API」のnearbySearchメソッドを使って、指定の場所から半径〇メートル以内のお店情報を取得できました。
ランキング表示するために、次は「お店情報を評価順に並べる」必要がありますね!

1

あなたへのおすすめ記事